mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-31 00:24:51 +00:00
app/ping: perform ICMP Echo to another 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 Echo handshake it prints a short statistic. By now, it can be used only with a static IP configuration. The size of the ICMP data field can be configured. It gets filled with the letters of the alphabet ('a' to 'z') repeatedly. Issue #2732
This commit is contained in:
parent
97317b0c95
commit
84a3fbd239
100
repos/os/run/ping.run
Normal file
100
repos/os/run/ping.run
Normal file
@ -0,0 +1,100 @@
|
||||
#
|
||||
# Build
|
||||
#
|
||||
|
||||
if {![have_include power_on/qemu]} {
|
||||
puts "Run script is only supported on Qemu"
|
||||
return 0
|
||||
}
|
||||
|
||||
set build_components {
|
||||
core
|
||||
init
|
||||
drivers/timer
|
||||
drivers/nic
|
||||
app/ping
|
||||
}
|
||||
|
||||
source ${genode_dir}/repos/base/run/platform_drv.inc
|
||||
append_platform_drv_build_components
|
||||
|
||||
build $build_components
|
||||
|
||||
create_boot_directory
|
||||
|
||||
#
|
||||
# Generate config
|
||||
#
|
||||
|
||||
append config {
|
||||
<config>
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="IO_PORT"/>
|
||||
<service name="PD"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="LOG"/>
|
||||
</parent-provides>
|
||||
<default-route>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
<default caps="100"/>}
|
||||
|
||||
append_platform_drv_config
|
||||
|
||||
append config {
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
<start name="nic_drv">
|
||||
<binary name="} [nic_drv_binary] {"/>
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<provides><service name="Nic"/></provides>
|
||||
</start>
|
||||
<start name="ping">
|
||||
<resource name="RAM" quantum="8M"/>
|
||||
<config src_ip="10.0.2.55"
|
||||
dst_ip="10.0.2.2"
|
||||
data_size="56"
|
||||
period_sec="1"
|
||||
verbose="no"
|
||||
count="3"/>
|
||||
<route>
|
||||
<service name="Nic"> <child name="nic_drv"/> </service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
</config>}
|
||||
|
||||
install_config $config
|
||||
|
||||
#
|
||||
# Boot modules
|
||||
#
|
||||
|
||||
# generic modules
|
||||
append boot_modules {
|
||||
core init
|
||||
timer
|
||||
} [nic_drv_binary] {
|
||||
ping
|
||||
ld.lib.so
|
||||
}
|
||||
|
||||
# platform-specific modules
|
||||
lappend_if [have_spec linux] boot_modules fb_sdl
|
||||
|
||||
append_platform_drv_boot_modules
|
||||
|
||||
build_boot_image $boot_modules
|
||||
|
||||
append_if [have_spec x86] qemu_args " -net nic,model=e1000 "
|
||||
append_if [have_spec lan9118] qemu_args " -net nic,model=lan9118 "
|
||||
|
||||
append qemu_args " -net user -nographic "
|
||||
|
||||
run_genode_until "\"ping\" exited with exit value 0.*\n" 25
|
58
repos/os/src/app/ping/README
Normal file
58
repos/os/src/app/ping/README
Normal file
@ -0,0 +1,58 @@
|
||||
|
||||
===================================================
|
||||
Perform and evaluate ICMP Echo with another IP host
|
||||
===================================================
|
||||
|
||||
|
||||
Brief
|
||||
#####
|
||||
|
||||
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
|
||||
Echo handshake it prints a short statistic. By now, it can be used only with a
|
||||
static IP configuration. The size of the ICMP data field can be configured. It
|
||||
gets filled with the letters of the alphabet ('a' to 'z') repeatedly.
|
||||
|
||||
|
||||
Configuration
|
||||
#############
|
||||
|
||||
This is an example configuration of the component which shows the default
|
||||
value for each attribute except 'config.dst_ip' and 'config.src_ip':
|
||||
|
||||
! <config src_ip="10.0.0.72"
|
||||
! dst_ip="10.0.0.24"
|
||||
! data_size="56"
|
||||
! period_sec="5"
|
||||
! verbose="no"
|
||||
! count="5" />
|
||||
|
||||
|
||||
This is a short description of the tags and attributes:
|
||||
|
||||
:config.src_ip:
|
||||
Mandatory. IP address of the component.
|
||||
|
||||
:config.dst_ip:
|
||||
Mandatory. IP address of the target host.
|
||||
|
||||
:config.data_size:
|
||||
Optional. Size of the ICMP data field.
|
||||
|
||||
:config.period_sec:
|
||||
Optional. Length of send interval in seconds.
|
||||
|
||||
:config.verbose:
|
||||
Optional. Toggles wether the component shall log debugging information.
|
||||
|
||||
:config.count:
|
||||
Optional. After how many successful pings the component exits successfully.
|
||||
|
||||
|
||||
Sessions
|
||||
########
|
||||
|
||||
This is an overview of the sessions required and provided by the
|
||||
component apart from the environment sessions:
|
||||
|
||||
* Requires one Timer session.
|
39
repos/os/src/app/ping/config.xsd
Normal file
39
repos/os/src/app/ping/config.xsd
Normal file
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
|
||||
<xs:simpleType name="Boolean">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="true" />
|
||||
<xs:enumeration value="yes" />
|
||||
<xs:enumeration value="1" />
|
||||
<xs:enumeration value="false" />
|
||||
<xs:enumeration value="no" />
|
||||
<xs:enumeration value="0" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType><!-- Boolean -->
|
||||
|
||||
<xs:simpleType name="Seconds">
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:minInclusive value="1"/>
|
||||
<xs:maxInclusive value="3600"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType><!-- Seconds -->
|
||||
|
||||
<xs:simpleType name="Ipv4_address">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType><!-- Ipv4_address -->
|
||||
|
||||
<xs:element name="config">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="verbose" type="Boolean" />
|
||||
<xs:attribute name="dst_ip" type="Ipv4_address" />
|
||||
<xs:attribute name="src_ip" type="Ipv4_address" />
|
||||
<xs:attribute name="period_sec" type="Seconds" />
|
||||
<xs:attribute name="count" type="xs:positiveInteger" />
|
||||
<xs:attribute name="data_size" type="xs:nonNegativeInteger" />
|
||||
</xs:complexType>
|
||||
</xs:element><!-- config -->
|
||||
|
||||
</xs:schema>
|
542
repos/os/src/app/ping/main.cc
Normal file
542
repos/os/src/app/ping/main.cc
Normal file
@ -0,0 +1,542 @@
|
||||
/*
|
||||
* \brief Test the reachability of a host on an IP network
|
||||
* \author Martin Stein
|
||||
* \date 2018-03-27
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <size_guard.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <net/ipv4.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/arp.h>
|
||||
#include <net/icmp.h>
|
||||
#include <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <nic_session/connection.h>
|
||||
#include <nic/packet_allocator.h>
|
||||
|
||||
using namespace Net;
|
||||
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,
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
class Main
|
||||
{
|
||||
private:
|
||||
|
||||
using Signal_handler = Genode::Signal_handler<Main>;
|
||||
using Periodic_timeout = Timer::Periodic_timeout<Main>;
|
||||
|
||||
enum { IPV4_TIME_TO_LIVE = 64 };
|
||||
enum { ICMP_ID = 1166 };
|
||||
enum { DEFAULT_ICMP_DATA_SZ = 56 };
|
||||
enum { DEFAULT_COUNT = 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;
|
||||
Attached_rom_dataspace _config_rom { _env, "config" };
|
||||
Xml_node _config { _config_rom.xml() };
|
||||
Timer::Connection _timer { _env };
|
||||
Microseconds _send_time { 0 };
|
||||
Periodic_timeout _period { _timer, *this, &Main::_send_ping,
|
||||
read_sec_attr(_config, "period_sec", DEFAULT_PERIOD_SEC) };
|
||||
Heap _heap { &_env.ram(), &_env.rm() };
|
||||
Nic::Packet_allocator _pkt_alloc { &_heap };
|
||||
Nic::Connection _nic { _env, &_pkt_alloc, BUF_SIZE, BUF_SIZE };
|
||||
Signal_handler _sink_ack { _env.ep(), *this, &Main::_ack_avail };
|
||||
Signal_handler _sink_submit { _env.ep(), *this, &Main::_ready_to_submit };
|
||||
Signal_handler _source_ack { _env.ep(), *this, &Main::_ready_to_ack };
|
||||
Signal_handler _source_submit { _env.ep(), *this, &Main::_packet_avail };
|
||||
bool const _verbose { _config.attribute_value("verbose", false) };
|
||||
Ipv4_address const _src_ip { _config.attribute_value("src_ip", Ipv4_address()) };
|
||||
Ipv4_address const _dst_ip { _config.attribute_value("dst_ip", Ipv4_address()) };
|
||||
Mac_address const _src_mac { _nic.mac_address() };
|
||||
Mac_address _dst_mac { };
|
||||
uint16_t _ip_id { 1 };
|
||||
uint16_t _icmp_seq { 1 };
|
||||
size_t const _icmp_data_sz { _config.attribute_value("data_size", (size_t)DEFAULT_ICMP_DATA_SZ) };
|
||||
unsigned long _count { _config.attribute_value("count", (unsigned long)DEFAULT_COUNT) };
|
||||
|
||||
void _handle_eth(void *const eth_base,
|
||||
size_t const eth_size,
|
||||
Packet_descriptor const &pkt);
|
||||
|
||||
void _handle_ip(Ethernet_frame ð,
|
||||
size_t const eth_size);
|
||||
|
||||
void _handle_icmp(Ipv4_packet &ip,
|
||||
size_t const ip_size);
|
||||
|
||||
void _handle_icmp_echo_reply(Ipv4_packet &ip,
|
||||
Icmp_packet &icmp,
|
||||
size_t icmp_data_sz);
|
||||
|
||||
void _handle_icmp_dst_unreachbl(Ipv4_packet &ip,
|
||||
Icmp_packet &icmp,
|
||||
size_t icmp_data_sz);
|
||||
|
||||
void _handle_arp(Ethernet_frame ð,
|
||||
size_t const eth_size);
|
||||
|
||||
void _broadcast_arp_request();
|
||||
|
||||
void _send_arp_reply(Ethernet_frame &req_eth,
|
||||
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);
|
||||
write_to_pkt(pkt_base);
|
||||
_source().submit_packet(pkt);
|
||||
if (_verbose) {
|
||||
log("snd ", *reinterpret_cast<Ethernet_frame *>(pkt_base)); }
|
||||
}
|
||||
catch (Net::Packet_stream_source::Packet_alloc_failed) {
|
||||
warning("failed to allocate packet"); }
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
struct Invalid_arguments : Exception { };
|
||||
struct Send_buffer_too_small : Exception { };
|
||||
|
||||
Main(Env &env);
|
||||
};
|
||||
|
||||
|
||||
Main::Main(Env &env) : _env(env)
|
||||
{
|
||||
/* exit unsuccessful if parameters are invalid */
|
||||
if (_src_ip == Ipv4_address() ||
|
||||
_dst_ip == Ipv4_address() ||
|
||||
_count == 0)
|
||||
{
|
||||
throw Invalid_arguments();
|
||||
}
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
||||
void Main::_handle_eth(void *const eth_base,
|
||||
size_t const eth_size,
|
||||
Packet_descriptor const &)
|
||||
{
|
||||
/* print receipt message */
|
||||
Ethernet_frame ð = *reinterpret_cast<Ethernet_frame *>(eth_base);
|
||||
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) {
|
||||
log("bad ETH destination"); }
|
||||
return;
|
||||
}
|
||||
/* select ETH sub-protocol */
|
||||
switch (eth.type()) {
|
||||
case Ethernet_frame::Type::ARP: _handle_arp(eth, eth_size); break;
|
||||
case Ethernet_frame::Type::IPV4: _handle_ip(eth, eth_size); break;
|
||||
default: ; }
|
||||
}
|
||||
|
||||
|
||||
void Main::_handle_ip(Ethernet_frame ð,
|
||||
size_t const eth_size)
|
||||
{
|
||||
/* drop packet if IP does not target us */
|
||||
size_t const ip_size = eth_size - sizeof(Ethernet_frame);
|
||||
Ipv4_packet &ip = *eth.data<Ipv4_packet>(ip_size);
|
||||
if (ip.dst() != _src_ip &&
|
||||
ip.dst() != Ipv4_packet::BROADCAST)
|
||||
{
|
||||
if (_verbose) {
|
||||
log("bad IP destination"); }
|
||||
return;
|
||||
}
|
||||
/* drop packet if IP checksum is invalid */
|
||||
if (Ipv4_packet::calculate_checksum(ip) != ip.checksum()) {
|
||||
if (_verbose) {
|
||||
log("bad IP checksum"); }
|
||||
return;
|
||||
}
|
||||
/* select IP sub-protocol */
|
||||
switch (ip.protocol()) {
|
||||
case Ipv4_packet::Protocol::ICMP: _handle_icmp(ip, ip_size);
|
||||
default: ; }
|
||||
}
|
||||
|
||||
|
||||
void Main::_handle_icmp_echo_reply(Ipv4_packet &ip,
|
||||
Icmp_packet &icmp,
|
||||
size_t icmp_data_sz)
|
||||
{
|
||||
/* check IP source */
|
||||
if (ip.src() != _dst_ip) {
|
||||
if (_verbose) {
|
||||
log("bad IP source"); }
|
||||
return;
|
||||
}
|
||||
/* check ICMP code */
|
||||
if (icmp.code() != Icmp_packet::Code::ECHO_REPLY) {
|
||||
if (_verbose) {
|
||||
log("bad ICMP type/code"); }
|
||||
return;
|
||||
}
|
||||
/* check ICMP identifier */
|
||||
uint16_t const icmp_id = icmp.query_id();
|
||||
if (icmp_id != ICMP_ID) {
|
||||
if (_verbose) {
|
||||
log("bad ICMP identifier"); }
|
||||
return;
|
||||
}
|
||||
/* check ICMP sequence number */
|
||||
uint16_t const icmp_seq = icmp.query_seq();
|
||||
if (icmp_seq != _icmp_seq) {
|
||||
if (_verbose) {
|
||||
log("bad ICMP sequence number"); }
|
||||
return;
|
||||
}
|
||||
/* check ICMP data size */
|
||||
if (icmp_data_sz != _icmp_data_sz) {
|
||||
if (_verbose) {
|
||||
log("bad ICMP data size"); }
|
||||
return;
|
||||
}
|
||||
/* check ICMP data */
|
||||
struct Data { char chr[0]; };
|
||||
Data &data = icmp.data<Data>(_icmp_data_sz);
|
||||
char chr = 'a';
|
||||
for (addr_t chr_id = 0; chr_id < icmp_data_sz; chr_id++) {
|
||||
if (data.chr[chr_id] != chr) {
|
||||
if (_verbose) {
|
||||
log("bad ICMP data"); }
|
||||
return;
|
||||
}
|
||||
chr = chr < 'z' ? chr + 1 : 'a';
|
||||
}
|
||||
/* calculate time since the request was sent */
|
||||
unsigned long time_us = _timer.curr_time().trunc_to_plain_us().value - _send_time.value;
|
||||
unsigned long const time_ms = time_us / 1000UL;
|
||||
time_us = time_us - time_ms * 1000UL;
|
||||
|
||||
/* print success message */
|
||||
log(icmp_data_sz + sizeof(Icmp_packet), " bytes from ", ip.src(),
|
||||
": icmp_seq=", icmp_seq, " ttl=", (unsigned long)IPV4_TIME_TO_LIVE,
|
||||
" time=", time_ms, ".", time_us ," ms");
|
||||
|
||||
/* raise ICMP sequence number and check exit condition */
|
||||
_icmp_seq++;
|
||||
_count--;
|
||||
if (!_count) {
|
||||
_env.parent().exit(0); }
|
||||
}
|
||||
|
||||
|
||||
void Main::_handle_icmp_dst_unreachbl(Ipv4_packet &ip,
|
||||
Icmp_packet &icmp,
|
||||
size_t icmp_data_sz)
|
||||
{
|
||||
/* drop packet if embedded IP checksum is invalid */
|
||||
Ipv4_packet &embed_ip = icmp.data<Ipv4_packet>(icmp_data_sz);
|
||||
if (Ipv4_packet::calculate_checksum(embed_ip) != embed_ip.checksum()) {
|
||||
if (_verbose) {
|
||||
log("bad IP checksum in payload of ICMP error"); }
|
||||
return;
|
||||
}
|
||||
/* drop packet if the ICMP error is not about ICMP */
|
||||
if (embed_ip.protocol() != Ipv4_packet::Protocol::ICMP) {
|
||||
if (_verbose) {
|
||||
log("bad IP protocol in payload of ICMP error"); }
|
||||
return;
|
||||
}
|
||||
/* drop packet if embedded ICMP identifier is invalid */
|
||||
size_t const embed_icmp_sz = embed_ip.total_length() - sizeof(Ipv4_packet);
|
||||
Icmp_packet &embed_icmp = *embed_ip.data<Icmp_packet>(embed_icmp_sz);
|
||||
if (embed_icmp.query_id() != ICMP_ID) {
|
||||
if (_verbose) {
|
||||
log("bad ICMP identifier in payload of ICMP error"); }
|
||||
return;
|
||||
}
|
||||
/* drop packet if embedded ICMP sequence number is invalid */
|
||||
uint16_t const embed_icmp_seq = embed_icmp.query_seq();
|
||||
if (embed_icmp_seq != _icmp_seq) {
|
||||
if (_verbose) {
|
||||
log("bad ICMP sequence number in payload of ICMP error"); }
|
||||
return;
|
||||
}
|
||||
log("From ", ip.src(), " icmp_seq=", embed_icmp_seq, " Destination Unreachable");
|
||||
}
|
||||
|
||||
|
||||
void Main::_handle_icmp(Ipv4_packet &ip,
|
||||
size_t const ip_size)
|
||||
{
|
||||
/* drop packet if ICMP checksum is invalid */
|
||||
size_t const icmp_sz = ip_size - sizeof(Ipv4_packet);
|
||||
Icmp_packet &icmp = *ip.data<Icmp_packet>(icmp_sz);
|
||||
size_t const icmp_data_sz = icmp_sz - sizeof(Icmp_packet);
|
||||
if (icmp.calc_checksum(icmp_data_sz) != icmp.checksum()) {
|
||||
if (_verbose) {
|
||||
log("bad ICMP checksum"); }
|
||||
return;
|
||||
}
|
||||
/* select ICMP type */
|
||||
switch (icmp.type()) {
|
||||
case Icmp_packet::Type::ECHO_REPLY: _handle_icmp_echo_reply(ip, icmp, icmp_data_sz); break;
|
||||
case Icmp_packet::Type::DST_UNREACHABLE: _handle_icmp_dst_unreachbl(ip, icmp, icmp_data_sz); break;
|
||||
default:
|
||||
if (_verbose) {
|
||||
log("bad ICMP type"); }
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Main::_handle_arp(Ethernet_frame ð,
|
||||
size_t const eth_size)
|
||||
{
|
||||
/* check ARP protocol- and hardware address type */
|
||||
Arp_packet &arp = *eth.data<Arp_packet>(eth_size - sizeof(Ethernet_frame));
|
||||
if (!arp.ethernet_ipv4()) {
|
||||
error("ARP for unknown protocol"); }
|
||||
|
||||
/* check ARP operation */
|
||||
switch (arp.opcode()) {
|
||||
case Arp_packet::REPLY:
|
||||
|
||||
/* check whether we waited for this ARP reply */
|
||||
if (_dst_mac != Mac_address() || arp.src_ip() != _dst_ip) {
|
||||
return; }
|
||||
|
||||
/* set destination MAC address and retry to ping */
|
||||
_dst_mac = arp.src_mac();
|
||||
_send_ping();
|
||||
return;
|
||||
|
||||
case Arp_packet::REQUEST:
|
||||
|
||||
/* check whether the ARP request targets us */
|
||||
if (arp.dst_ip() != _src_ip) {
|
||||
return; }
|
||||
|
||||
_send_arp_reply(eth, arp);
|
||||
|
||||
default: ; }
|
||||
}
|
||||
|
||||
|
||||
void Main::_ready_to_submit()
|
||||
{
|
||||
while (_sink().packet_avail()) {
|
||||
|
||||
Packet_descriptor const pkt = _sink().get_packet();
|
||||
if (!pkt.size()) {
|
||||
continue; }
|
||||
|
||||
_handle_eth(_sink().packet_content(pkt), pkt.size(), pkt);
|
||||
|
||||
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,
|
||||
Arp_packet &req_arp)
|
||||
{
|
||||
enum {
|
||||
ETH_HDR_SZ = sizeof(Ethernet_frame),
|
||||
ETH_DAT_SZ = sizeof(Arp_packet) + ETH_HDR_SZ >= Ethernet_frame::MIN_SIZE ?
|
||||
sizeof(Arp_packet) :
|
||||
Ethernet_frame::MIN_SIZE - ETH_HDR_SZ,
|
||||
ETH_CRC_SZ = sizeof(uint32_t),
|
||||
PKT_SIZE = ETH_HDR_SZ + ETH_DAT_SZ + ETH_CRC_SZ,
|
||||
};
|
||||
_send(PKT_SIZE, [&] (void *pkt_base) {
|
||||
|
||||
/* write Ethernet header */
|
||||
Ethernet_frame ð = *reinterpret_cast<Ethernet_frame *>(pkt_base);
|
||||
eth.dst(req_eth.src());
|
||||
eth.src(_src_mac);
|
||||
eth.type(Ethernet_frame::Type::ARP);
|
||||
|
||||
/* write ARP header */
|
||||
Arp_packet &arp = *eth.data<Arp_packet>(PKT_SIZE - sizeof(Ethernet_frame));
|
||||
arp.hardware_address_type(Arp_packet::ETHERNET);
|
||||
arp.protocol_address_type(Arp_packet::IPV4);
|
||||
arp.hardware_address_size(sizeof(Mac_address));
|
||||
arp.protocol_address_size(sizeof(Ipv4_address));
|
||||
arp.opcode(Arp_packet::REPLY);
|
||||
arp.src_mac(_src_mac);
|
||||
arp.src_ip(_src_ip);
|
||||
arp.dst_mac(req_eth.src());
|
||||
arp.dst_ip(req_arp.src_ip());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Main::_broadcast_arp_request()
|
||||
{
|
||||
enum {
|
||||
ETH_HDR_SZ = sizeof(Ethernet_frame),
|
||||
ETH_DAT_SZ = sizeof(Arp_packet) + ETH_HDR_SZ >= Ethernet_frame::MIN_SIZE ?
|
||||
sizeof(Arp_packet) :
|
||||
Ethernet_frame::MIN_SIZE - ETH_HDR_SZ,
|
||||
ETH_CRC_SZ = sizeof(uint32_t),
|
||||
PKT_SIZE = ETH_HDR_SZ + ETH_DAT_SZ + ETH_CRC_SZ,
|
||||
};
|
||||
_send(PKT_SIZE, [&] (void *pkt_base) {
|
||||
|
||||
/* write Ethernet header */
|
||||
Ethernet_frame ð = *reinterpret_cast<Ethernet_frame *>(pkt_base);
|
||||
eth.dst(Mac_address(0xff));
|
||||
eth.src(_src_mac);
|
||||
eth.type(Ethernet_frame::Type::ARP);
|
||||
|
||||
/* write ARP header */
|
||||
Arp_packet &arp = *eth.data<Arp_packet>(PKT_SIZE - sizeof(Ethernet_frame));
|
||||
arp.hardware_address_type(Arp_packet::ETHERNET);
|
||||
arp.protocol_address_type(Arp_packet::IPV4);
|
||||
arp.hardware_address_size(sizeof(Mac_address));
|
||||
arp.protocol_address_size(sizeof(Ipv4_address));
|
||||
arp.opcode(Arp_packet::REQUEST);
|
||||
arp.src_mac(_src_mac);
|
||||
arp.src_ip(_src_ip);
|
||||
arp.dst_mac(Mac_address(0xff));
|
||||
arp.dst_ip(_dst_ip);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Main::_send_ping(Duration)
|
||||
{
|
||||
if (_dst_mac == Mac_address()) {
|
||||
_broadcast_arp_request();
|
||||
return;
|
||||
}
|
||||
size_t const buf_sz = sizeof(Ethernet_frame) + sizeof(Ipv4_packet) +
|
||||
sizeof(Icmp_packet) + _icmp_data_sz;
|
||||
|
||||
_send(buf_sz, [&] (void *pkt_base) {
|
||||
|
||||
/* create ETH header */
|
||||
Size_guard<Send_buffer_too_small> size(buf_sz);
|
||||
size.add(sizeof(Ethernet_frame));
|
||||
Ethernet_frame ð = *reinterpret_cast<Ethernet_frame *>(pkt_base);
|
||||
eth.dst(_dst_mac);
|
||||
eth.src(_src_mac);
|
||||
eth.type(Ethernet_frame::Type::IPV4);
|
||||
|
||||
/* create IP header */
|
||||
size_t const ip_off = size.curr();
|
||||
Ipv4_packet &ip = *eth.data<Ipv4_packet>(size.left());
|
||||
size.add(sizeof(Ipv4_packet));
|
||||
ip.header_length(sizeof(Ipv4_packet) / 4);
|
||||
ip.version(4);
|
||||
ip.diff_service(0);
|
||||
ip.ecn(0);
|
||||
ip.identification(0);
|
||||
ip.flags(0);
|
||||
ip.fragment_offset(0);
|
||||
ip.time_to_live(IPV4_TIME_TO_LIVE);
|
||||
ip.protocol(Ipv4_packet::Protocol::ICMP);
|
||||
ip.src(_src_ip);
|
||||
ip.dst(_dst_ip);
|
||||
|
||||
/* create ICMP header */
|
||||
Icmp_packet &icmp = *ip.data<Icmp_packet>(size.left());
|
||||
size.add(sizeof(Icmp_packet) + _icmp_data_sz);
|
||||
icmp.type(Icmp_packet::Type::ECHO_REQUEST);
|
||||
icmp.code(Icmp_packet::Code::ECHO_REQUEST);
|
||||
icmp.query_id(ICMP_ID);
|
||||
icmp.query_seq(_icmp_seq);
|
||||
|
||||
/* fill ICMP data with characters from 'a' to 'w' */
|
||||
struct Data { char chr[0]; };
|
||||
Data &data = icmp.data<Data>(_icmp_data_sz);
|
||||
char chr = 'a';
|
||||
for (addr_t chr_id = 0; chr_id < _icmp_data_sz; chr_id++) {
|
||||
data.chr[chr_id] = chr;
|
||||
chr = chr < 'z' ? chr + 1 : 'a';
|
||||
}
|
||||
/* fill in header values that require the packet to be complete */
|
||||
icmp.checksum(icmp.calc_checksum(_icmp_data_sz));
|
||||
ip.total_length(size.curr() - ip_off);
|
||||
ip.checksum(Ipv4_packet::calculate_checksum(ip));
|
||||
});
|
||||
_send_time = _timer.curr_time().trunc_to_plain_us();
|
||||
}
|
||||
|
||||
|
||||
void Component::construct(Env &env)
|
||||
{
|
||||
/* XXX execute constructors of global statics */
|
||||
env.exec_static_constructors();
|
||||
|
||||
static Main main(env);
|
||||
}
|
43
repos/os/src/app/ping/size_guard.h
Normal file
43
repos/os/src/app/ping/size_guard.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* \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>
|
||||
|
||||
template <typename EXCEPTION>
|
||||
class Size_guard
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::size_t _curr { 0 };
|
||||
Genode::size_t const _max;
|
||||
|
||||
public:
|
||||
|
||||
Size_guard(Genode::size_t max) : _max(max) { }
|
||||
|
||||
void add(Genode::size_t size)
|
||||
{
|
||||
Genode::size_t const new_size = _curr + size;
|
||||
if (new_size > _max) { throw EXCEPTION(); }
|
||||
_curr = new_size;
|
||||
}
|
||||
|
||||
Genode::size_t curr() const { return _curr; }
|
||||
Genode::size_t left() const { return _max - _curr; }
|
||||
};
|
||||
|
||||
#endif /* _SIZE_GUARD_H_ */
|
9
repos/os/src/app/ping/target.mk
Normal file
9
repos/os/src/app/ping/target.mk
Normal file
@ -0,0 +1,9 @@
|
||||
TARGET = ping
|
||||
|
||||
LIBS += base net
|
||||
|
||||
SRC_CC += main.cc
|
||||
|
||||
INC_DIR += $(PRG_DIR)
|
||||
|
||||
CONFIG_XSD = config.xsd
|
Loading…
x
Reference in New Issue
Block a user