udp_log: send LOG as UDP packets

This commit is contained in:
Johannes Schlatow
2018-11-02 15:08:14 +01:00
committed by Norman Feske
parent 795b2db59f
commit c87b2f2eb9
5 changed files with 449 additions and 0 deletions

77
run/udp_log.run Normal file
View File

@@ -0,0 +1,77 @@
if {[expr [have_spec linux]]} {
puts "\n Run script is not supported on this platform. \n"; exit 0 }
#
# Test logging to file system
#
set build_components {
core init timer
proxy/udp_log
test/bomb
drivers/nic
}
source ${genode_dir}/repos/base/run/platform_drv.inc
append_platform_drv_build_components
build $build_components
create_boot_directory
install_config {
<config prio_levels="2">
<parent-provides>
<service name="CPU"/>
<service name="LOG"/>
<service name="PD"/>
<service name="RM"/>
<service name="ROM"/>
<service name="IO_PORT"/>
<service name="IO_MEM"/>
<service name="IRQ"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>
<start name="timer">
<resource name="CPU" quantum="10"/>
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="nic_drv">
<binary name="} [nic_drv_binary] {" />
<resource name="RAM" quantum="2M"/>
<provides><service name="Nic"/></provides>
</start>
<start name="udp_log">
<resource name="RAM" quantum="2M"/>
<provides><service name="LOG"/></provides>
<config src_ip="192.168.42.10" verbose="yes">
<default-policy ip="192.168.42.11" />
</config>
</start>
<start name="bomb-master" priority="-1" caps="500">
<binary name="bomb"/>
<resource name="CPU" quantum="90"/>
<resource name="RAM" quantum="1G"/>
<route>
<any-service> <any-child/> <parent/> </any-service>
</route>
<config rounds="10" generations="1" sleep="1000"/>
</start>
</config>}
append boot_modules {
core init ld.lib.so bomb timer udp_log
} [nic_drv_binary] {
}
append_platform_drv_boot_modules
build_boot_image $boot_modules
append qemu_args " -nographic"
append qemu_args " -net tap,ifname=tap2 "
run_genode_until forever

20
src/proxy/udp_log/README Normal file
View File

@@ -0,0 +1,20 @@
udp_log is a LOG server that sends client log messages as UDP
packets to a destination IP and port.
This component implements the standard notion of session policies.
The policy specifies the destination IP, UDP port and MAC address
as shown in the following example that shows the default values.
! <start name="udp_log">
! <resource name="RAM" quantum="1M"/>
! <provides> <service name="LOG"/> </provides>
! <config src_ip="0.0.0.0" verbose="no">
! <default_policy ip="0.0.0.0" mac="ff:ff:ff:ff:ff:ff" port="9" />
! </config>
! </start>
The component also gets its source IP address from the config ROM.
The verbose mode acts as a pass-through mode of the LOG messages to the
component's LOG session.
The UDP packets can be received with netcat or with log_udp.

187
src/proxy/udp_log/logger.h Normal file
View File

@@ -0,0 +1,187 @@
/*
* \brief Network logger
* \author Johannes Schlatow
* \date 2018-11-01
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <base/log.h>
#include <util/xml_node.h>
#include <net/udp.h>
#include <nic/packet_allocator.h>
#include <nic_session/connection.h>
using namespace Net;
namespace Udp_log {
using Genode::size_t;
using Genode::Xml_node;
using Nic::Packet_stream_source;
using Nic::Packet_descriptor;
class Payload;
template <typename MSG, typename PREFIX> class Logger;
};
class Udp_log::Payload
{
private:
char _data[0];
public:
void set(char const *p, size_t plen, char const *s, size_t len, Size_guard &size_guard)
{
/* write prefix */
size_guard.consume_head(plen);
Genode::memcpy(_data, p, plen);
/* write string */
size_guard.consume_head(len);
Genode::memcpy(&_data[plen], s, len);
/* zero-out unconsumed data */
size_t const unconsumed = size_guard.unconsumed();
size_guard.consume_head(unconsumed);
Genode::memset(&_data[plen+len], 0, unconsumed);
}
};
template <typename MSG, typename PREFIX>
class Udp_log::Logger
{
private:
enum {
PACKET_SIZE = 512,
BUF_SIZE = Nic::Session::QUEUE_SIZE * PACKET_SIZE
};
Ipv4_address const _default_ip_address { (Genode::uint8_t)0x00 };
Nic::Packet_allocator _tx_block_alloc;
Nic::Connection _nic;
Mac_address _src_mac { _nic.mac_address() };
Ipv4_address _src_ip;
Port const _src_port { 51234 };
bool _verbose { false };
bool _chksum_offload { false };
Genode::Signal_handler<Logger> _source_ack;
Genode::Signal_handler<Logger> _source_submit;
/**
* acknowledgement queue not empty anymore
*/
void _ready_to_ack()
{
/* check for acknowledgements */
while (source()->ack_avail())
source()->release_packet(source()->get_acked_packet());
}
/**
* submit queue not full anymore
*
* by now, packets are dropped if submit queue is full
*/
void _packet_avail() { }
Packet_stream_source< ::Nic::Session::Policy> * source() {
return _nic.tx(); }
public:
Logger(Genode::Env &env, Genode::Allocator &alloc, Xml_node config)
:
_tx_block_alloc(&alloc),
_nic(env, &_tx_block_alloc, BUF_SIZE, BUF_SIZE),
_src_ip (config.attribute_value("src_ip", _default_ip_address)),
_verbose(config.attribute_value("verbose", _verbose)),
_chksum_offload(config.attribute_value("chksum_offload", _chksum_offload)),
_source_ack(env.ep(), *this, &Logger::_ready_to_ack),
_source_submit(env.ep(), *this, &Logger::_packet_avail)
{
_nic.tx_channel()->sigh_ack_avail(_source_ack);
_nic.tx_channel()->sigh_ready_to_submit(_source_submit);
}
size_t write(PREFIX const &prefix, MSG const &string,
Ipv4_address const &ipaddr,
Port const &port,
Mac_address const &mac)
{
if (!_nic.link_state()) {
return 0;
}
enum {
HDR_SZ = sizeof(Ethernet_frame) + sizeof(Ipv4_packet) + sizeof(Udp_packet),
MIN_DATA_SZ = Ethernet_frame::MIN_SIZE - HDR_SZ,
};
size_t const packet_size = HDR_SZ + Genode::max((size_t)MIN_DATA_SZ,
string.size() + prefix.length());
try {
/* copy and submit packet */
Packet_descriptor packet = source()->alloc_packet(packet_size);
Size_guard size_guard(packet_size);
void *base = source()->packet_content(packet);
/* create ETH header */
Ethernet_frame &eth = Ethernet_frame::construct_at(base, size_guard);
eth.dst(mac);
eth.src(_src_mac);
eth.type(Ethernet_frame::Type::IPV4);
/* create IP header */
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(_src_ip);
ip.dst(ipaddr);
/* 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(_src_port);
udp.dst_port(port);
/* write payload */
Payload &payload = udp.construct_at_data<Payload>(size_guard);
payload.set(prefix.string(), prefix.length()-1,
string.string(), string.size(), size_guard);
/* fill in header values that need the packet to be complete already */
udp.length(size_guard.head_size() - udp_off);
if (!_chksum_offload)
udp.update_checksum(ip.src(), ip.dst());
ip.total_length(size_guard.head_size() - ip_off);
ip.update_checksum();
source()->submit_packet(packet);
} catch(Packet_stream_source<Nic::Session::Policy>::Packet_alloc_failed) {
Genode::warning("Packet dropped");
}
if (_verbose)
Genode::log(prefix, Genode::Cstring(string.string(), string.size()-2));
return string.size();
}
};

162
src/proxy/udp_log/main.cc Normal file
View File

@@ -0,0 +1,162 @@
/*
* \brief LOG server that sends data via UDP.
* \author Johannes Schlatow
* \date 2018-11-01
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <base/env.h>
#include <base/heap.h>
#include <base/log.h>
#include <net/udp.h>
#include <base/component.h>
#include <base/attached_rom_dataspace.h>
#include <log_session/log_session.h>
#include <root/component.h>
#include <os/session_policy.h>
#include "logger.h"
using namespace Net;
namespace Udp_log {
using Genode::size_t;
using Genode::Attached_rom_dataspace;
using Genode::Xml_node;
using Genode::Log_session;
class Session_component;
class Root;
struct Main;
};
class Udp_log::Session_component : public Genode::Rpc_object<Log_session>
{
public:
typedef Genode::String<Genode::Session_label::capacity()+3> Prefix;
private:
Genode::Env &_env;
Logger<String,Prefix> &_logger;
Prefix _prefix;
Mac_address const _default_mac_address { (Genode::uint8_t)0xff };
Ipv4_address const _default_ip_address { (Genode::uint8_t)0x00 };
Port const _default_port { 9 };
Mac_address const _dst_mac;
Ipv4_address const _dst_ip;
Port const _dst_port;
public:
Session_component(Genode::Env &env, Logger<String,Prefix> &logger,
Genode::Session_label const &label,
Xml_node policy)
:
_env(env), _logger(logger), _prefix("[", label.string(), "] "),
_dst_mac (policy.attribute_value("mac", _default_mac_address)),
_dst_ip (policy.attribute_value("ip", _default_ip_address)),
_dst_port(policy.attribute_value("port", _default_port))
{ }
/* LOG session implementation */
size_t write(String const &string) override
{
/* TODO implement ARP, i.e. do not send LOG packets until MAC is known
* for this we need the following modifications:
* - add optional gateway attribute to config to allow specifying the next hop
* if the dst_ip is not the next hop
* - implement an Arp_resolver class on root level that send ARP requests using
* the logger and handles ARP responses
* - ARP responses are delivered to all session components
* - a session component stores a single address
* - a session component stores uses the broadcast MAC until the address is resolved
*/
return _logger.write(_prefix, string, _dst_ip, _dst_port, _dst_mac);
}
};
class Udp_log::Root : public Genode::Root_component<Session_component>
{
private:
Genode::Env &_env;
Genode::Allocator &_alloc;
Attached_rom_dataspace _config = { _env, "config" };
Logger<Log_session::String,
Session_component::Prefix>
_logger = { _env, _alloc, _config.xml() };
protected:
Session_component *_create_session(const char *args) override
{
using namespace Genode;
try {
Session_label const label = label_from_args(args);
Session_policy policy(label, _config.xml());
return new (Root::md_alloc())
Session_component(_env, _logger, label, policy);
}
catch (Session_policy::No_policy_defined) {
Genode::warning("Missing policy.");
return 0;
}
catch (Out_of_ram) {
Genode::warning("insufficient 'ram_quota'");
throw Insufficient_ram_quota();
}
catch (Out_of_caps) {
Genode::warning("insufficient 'cap_quota'");
throw Insufficient_cap_quota();
}
}
public:
Root(Genode::Env &env, Genode::Allocator &md_alloc)
: Genode::Root_component<Session_component>(&env.ep().rpc_ep(), &md_alloc),
_env(env), _alloc(md_alloc)
{ }
};
struct Udp_log::Main
{
Genode::Env &env;
Genode::Heap heap = { &env.ram(), &env.rm() };
Root root = { env, heap };
Main(Genode::Env &env) : env(env)
{
env.parent().announce(env.ep().manage(root));
}
};
namespace Component {
Genode::size_t stack_size() { return 2*1024*sizeof(long); }
void construct(Genode::Env &env)
{
env.exec_static_constructors();
static Udp_log::Main main(env);
}
}

View File

@@ -0,0 +1,3 @@
TARGET = udp_log
SRC_CC = main.cc
LIBS = base net