ping: support UDP

Ping via specific UDP port instead of ICMP Echo with the two new configuration
attributes 'protocol' and 'dst_port'.

Issue #2775
This commit is contained in:
Martin Stein 2018-04-18 14:46:10 +02:00 committed by Christian Helmuth
parent d93fda594a
commit 2d229a2b72
4 changed files with 214 additions and 43 deletions

View File

@ -1,7 +1,7 @@
The 'ping' component continuously sends ICMP Echo requests to a given IP host The 'ping' component continuously sends ICMP Echo or UDP requests to a given
and waits for the corresponding ICMP Echo replies. For each successfull ICMP IP host and waits for the corresponding ICMP Echo or UDP replies. For each
Echo handshake it prints a short statistic. The ICMP data field gets filled successfull ICMP Echo or UDP handshake it prints a short statistic. The ICMP
with the letters of the alphabet ('a' to 'z') repeatedly. data field gets filled with the letters of the alphabet ('a' to 'z') repeatedly.
Configuration Configuration
@ -12,6 +12,8 @@ value for each attribute except 'config.dst_ip' and 'config.interface':
! <config interface="10.0.0.72/24" ! <config interface="10.0.0.72/24"
! dst_ip="10.0.0.24" ! dst_ip="10.0.0.24"
! dst_port="50000"
! protocol="icmp"
! period_sec="5" ! period_sec="5"
! verbose="no" ! verbose="no"
! count="5" /> ! count="5" />
@ -37,6 +39,12 @@ This is a short description of the tags and attributes:
:config.count: :config.count:
Optional. After how many successful pings the component exits successfully. Optional. After how many successful pings the component exits successfully.
:config.dst_port:
Optional. Destination port resp. ICMP query ID to use.
:config.protocol:
Optional. Protocol to ping with. Can be one of 'icmp', 'udp'.
Sessions Sessions
~~~~~~~~ ~~~~~~~~

View File

@ -12,6 +12,20 @@
</xs:restriction> </xs:restriction>
</xs:simpleType><!-- Boolean --> </xs:simpleType><!-- Boolean -->
<xs:simpleType name="Port">
<xs:restriction base="xs:integer">
<xs:minInclusive value="0"/>
<xs:maxInclusive value="65535"/>
</xs:restriction>
</xs:simpleType><!-- Port -->
<xs:simpleType name="Protocol">
<xs:restriction base="xs:string">
<xs:enumeration value="icmp" />
<xs:enumeration value="udp" />
</xs:restriction>
</xs:simpleType><!-- Protocol -->
<xs:simpleType name="Seconds"> <xs:simpleType name="Seconds">
<xs:restriction base="xs:integer"> <xs:restriction base="xs:integer">
<xs:minInclusive value="1"/> <xs:minInclusive value="1"/>
@ -35,6 +49,8 @@
<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="dst_port" type="Port" />
<xs:attribute name="protocol" type="Protocol" />
<xs:attribute name="interface" type="Ipv4_address_prefix" /> <xs:attribute name="interface" type="Ipv4_address_prefix" />
<xs:attribute name="gateway" type="Ipv4_address" /> <xs:attribute name="gateway" type="Ipv4_address" />
<xs:attribute name="period_sec" type="Seconds" /> <xs:attribute name="period_sec" type="Seconds" />

View File

@ -15,6 +15,7 @@
#include <nic.h> #include <nic.h>
#include <ipv4_config.h> #include <ipv4_config.h>
#include <dhcp_client.h> #include <dhcp_client.h>
#include <protocol.h>
/* Genode includes */ /* Genode includes */
#include <net/ipv4.h> #include <net/ipv4.h>
@ -51,10 +52,11 @@ class Main : public Nic_handler,
using Periodic_timeout = Timer::Periodic_timeout<Main>; using Periodic_timeout = Timer::Periodic_timeout<Main>;
enum { IPV4_TIME_TO_LIVE = 64 }; enum { IPV4_TIME_TO_LIVE = 64 };
enum { ICMP_ID = 1166 }; enum { DEFAULT_DST_PORT = 50000 };
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 { SRC_PORT = 50000 };
Env &_env; Env &_env;
Attached_rom_dataspace _config_rom { _env, "config" }; Attached_rom_dataspace _config_rom { _env, "config" };
@ -75,6 +77,8 @@ class Main : public Nic_handler,
Reconstructible<Ipv4_config> _ip_config { _config.attribute_value("interface", Ipv4_address_prefix()), Reconstructible<Ipv4_config> _ip_config { _config.attribute_value("interface", Ipv4_address_prefix()),
_config.attribute_value("gateway", Ipv4_address()), _config.attribute_value("gateway", Ipv4_address()),
Ipv4_address() }; Ipv4_address() };
Protocol const _protocol { _config.attribute_value("protocol", Protocol::ICMP) };
Port const _dst_port { _config.attribute_value("dst_port", Port(DEFAULT_DST_PORT)) };
void _handle_ip(Ethernet_frame &eth, void _handle_ip(Ethernet_frame &eth,
Size_guard &size_guard); Size_guard &size_guard);
@ -82,6 +86,9 @@ class Main : public Nic_handler,
void _handle_icmp(Ipv4_packet &ip, void _handle_icmp(Ipv4_packet &ip,
Size_guard &size_guard); Size_guard &size_guard);
void _handle_udp(Ipv4_packet &ip,
Size_guard &size_guard);
void _handle_icmp_echo_reply(Ipv4_packet &ip, void _handle_icmp_echo_reply(Ipv4_packet &ip,
Icmp_packet &icmp, Icmp_packet &icmp,
Size_guard &size_guard); Size_guard &size_guard);
@ -203,7 +210,8 @@ void Main::_handle_ip(Ethernet_frame &eth,
} }
/* select IP sub-protocol */ /* select IP sub-protocol */
switch (ip.protocol()) { switch (ip.protocol()) {
case Ipv4_packet::Protocol::ICMP: _handle_icmp(ip, size_guard); case Ipv4_packet::Protocol::ICMP: _handle_icmp(ip, size_guard); break;
case Ipv4_packet::Protocol::UDP: _handle_udp(ip, size_guard); break;
default: ; } default: ; }
} }
@ -212,6 +220,12 @@ void Main::_handle_icmp_echo_reply(Ipv4_packet &ip,
Icmp_packet &icmp, Icmp_packet &icmp,
Size_guard &size_guard) Size_guard &size_guard)
{ {
/* drop packet if our request was no ICMP */
if (_protocol != Protocol::ICMP) {
if (_verbose) {
log("bad IP protocol"); }
return;
}
/* check IP source */ /* check IP source */
if (ip.src() != _dst_ip) { if (ip.src() != _dst_ip) {
if (_verbose) { if (_verbose) {
@ -226,7 +240,7 @@ void Main::_handle_icmp_echo_reply(Ipv4_packet &ip,
} }
/* check ICMP identifier */ /* check ICMP identifier */
uint16_t const icmp_id = icmp.query_id(); uint16_t const icmp_id = icmp.query_id();
if (icmp_id != ICMP_ID) { if (icmp_id != _dst_port.value) {
if (_verbose) { if (_verbose) {
log("bad ICMP identifier"); } log("bad ICMP identifier"); }
return; return;
@ -285,27 +299,58 @@ void Main::_handle_icmp_dst_unreachbl(Ipv4_packet &ip,
log("bad IP checksum in payload of ICMP error"); } log("bad IP checksum in payload of ICMP error"); }
return; return;
} }
/* drop packet if the ICMP error is not about ICMP */ /* select IP-encapsulated protocol */
if (embed_ip.protocol() != Ipv4_packet::Protocol::ICMP) { switch (_protocol) {
if (_verbose) { case Protocol::ICMP:
log("bad IP protocol 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 */
Icmp_packet &embed_icmp = embed_ip.data<Icmp_packet>(size_guard);
if (embed_icmp.query_id() != _dst_port.value) {
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");
break;
}
case Protocol::UDP:
{
/* drop packet if the ICMP error is not about UDP */
if (embed_ip.protocol() != Ipv4_packet::Protocol::UDP) {
if (_verbose) {
log("bad IP protocol in payload of ICMP error"); }
return;
}
/* drop packet if embedded UDP source port is invalid */
Udp_packet &embed_udp = embed_ip.data<Udp_packet>(size_guard);
if (embed_udp.src_port().value != SRC_PORT) {
if (_verbose) {
log("bad UDP source port in payload of ICMP error"); }
return;
}
/* drop packet if embedded UDP destination port is invalid */
if (embed_udp.dst_port().value != _dst_port.value) {
if (_verbose) {
log("bad UDP destination port in payload of ICMP error"); }
return;
}
log("From ", ip.src(), " Destination Unreachable");
break;
}
} }
/* drop packet if embedded ICMP identifier is invalid */
Icmp_packet &embed_icmp = embed_ip.data<Icmp_packet>(size_guard);
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");
} }
@ -331,6 +376,50 @@ void Main::_handle_icmp(Ipv4_packet &ip,
} }
void Main::_handle_udp(Ipv4_packet &ip,
Size_guard &size_guard)
{
/* drop packet if our request was no UDP */
if (_protocol != Protocol::UDP) {
if (_verbose) {
log("bad IP protocol"); }
return;
}
/* drop packet if UDP checksum is invalid */
Udp_packet &udp = ip.data<Udp_packet>(size_guard);
if (udp.checksum_error(ip.src(), ip.dst())) {
if (_verbose) {
log("bad UDP checksum"); }
return;
}
/* drop packet if UDP source port is invalid */
if (udp.src_port().value != _dst_port.value) {
if (_verbose) {
log("bad UDP source port"); }
return;
}
/* drop packet if UDP destination port is invalid */
if (udp.dst_port().value != SRC_PORT) {
if (_verbose) {
log("bad UDP destination port"); }
return;
}
/* 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(udp.length(), " bytes from ", ip.src(), " ttl=", (unsigned long)IPV4_TIME_TO_LIVE,
" time=", time_ms, ".", time_us ," ms");
/* check exit condition */
_count--;
if (!_count) {
_env.parent().exit(0); }
}
void Main::_handle_arp(Ethernet_frame &eth, void Main::_handle_arp(Ethernet_frame &eth,
Size_guard &size_guard) Size_guard &size_guard)
{ {
@ -450,27 +539,53 @@ void Main::_send_ping(Duration)
ip.header_length(sizeof(Ipv4_packet) / 4); ip.header_length(sizeof(Ipv4_packet) / 4);
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.src(ip_config().interface.address); ip.src(ip_config().interface.address);
ip.dst(_dst_ip); ip.dst(_dst_ip);
/* create ICMP header */ /* select IP-encapsulated protocol */
Icmp_packet &icmp = ip.construct_at_data<Icmp_packet>(size_guard); switch (_protocol) {
icmp.type(Icmp_packet::Type::ECHO_REQUEST); case Protocol::ICMP:
icmp.code(Icmp_packet::Code::ECHO_REQUEST); {
icmp.query_id(ICMP_ID); /* adapt IP header to ICMP */
icmp.query_seq(_icmp_seq); ip.protocol(Ipv4_packet::Protocol::ICMP);
/* fill ICMP data with characters from 'a' to 'z' */ /* create ICMP header */
struct Data { char chr[ICMP_DATA_SIZE]; }; Icmp_packet &icmp = ip.construct_at_data<Icmp_packet>(size_guard);
Data &data = icmp.data<Data>(size_guard); icmp.type(Icmp_packet::Type::ECHO_REQUEST);
char chr = 'a'; icmp.code(Icmp_packet::Code::ECHO_REQUEST);
for (addr_t chr_id = 0; chr_id < ICMP_DATA_SIZE; chr_id++) { icmp.query_id(_dst_port.value);
data.chr[chr_id] = chr; icmp.query_seq(_icmp_seq);
chr = chr < 'z' ? chr + 1 : 'a';
/* fill ICMP data with characters from 'a' to 'z' */
struct Data { char chr[ICMP_DATA_SIZE]; };
Data &data = icmp.data<Data>(size_guard);
char chr = 'a';
for (addr_t chr_id = 0; chr_id < ICMP_DATA_SIZE; chr_id++) {
data.chr[chr_id] = chr;
chr = chr < 'z' ? chr + 1 : 'a';
}
/* finish ICMP header */
icmp.update_checksum(ICMP_DATA_SIZE);
break;
}
case Protocol::UDP:
{
/* adapt IP header to UDP */
ip.protocol(Ipv4_packet::Protocol::UDP);
/* create UDP header */
size_t const udp_off = size_guard.head_size();
Udp_packet &udp = ip.construct_at_data<Udp_packet>(size_guard);
udp.src_port(Port(SRC_PORT));
udp.dst_port(_dst_port);
/* finish UDP header */
udp.length(size_guard.head_size() - udp_off);
udp.update_checksum(ip.src(), ip.dst());
break;
}
} }
/* fill in header values that require the packet to be complete */ /* finish IP header */
icmp.update_checksum(ICMP_DATA_SIZE);
ip.total_length(size_guard.head_size() - ip_off); ip.total_length(size_guard.head_size() - ip_off);
ip.update_checksum(); ip.update_checksum();
}); });

View File

@ -0,0 +1,32 @@
/*
* \brief Supported protocols
* \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.
*/
#ifndef __PROTOCOL_H_
#define __PROTOCOL_H_
/* Genode includes */
#include <base/stdint.h>
namespace Genode { enum class Protocol : uint16_t { ICMP, UDP }; }
namespace Genode
{
inline size_t ascii_to(char const *s, Protocol &result)
{
if (!strcmp(s, "icmp", 4)) { result = Protocol::ICMP; return 4; }
if (!strcmp(s, "udp", 3)) { result = Protocol::UDP; return 3; }
return 0;
}
}
#endif /* __PROTOCOL_H_ */