diff --git a/run/udp_log.run b/run/udp_log.run new file mode 100644 index 0000000..03fd8c9 --- /dev/null +++ b/run/udp_log.run @@ -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 { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +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 diff --git a/src/proxy/udp_log/README b/src/proxy/udp_log/README new file mode 100644 index 0000000..850efc8 --- /dev/null +++ b/src/proxy/udp_log/README @@ -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. + +! +! +! +! +! +! +! + +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. diff --git a/src/proxy/udp_log/logger.h b/src/proxy/udp_log/logger.h new file mode 100644 index 0000000..1001304 --- /dev/null +++ b/src/proxy/udp_log/logger.h @@ -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 +#include + +#include +#include +#include + +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 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 +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 _source_ack; + Genode::Signal_handler _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 ð = 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(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(size_guard); + udp.src_port(_src_port); + udp.dst_port(port); + + /* write payload */ + Payload &payload = udp.construct_at_data(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::Packet_alloc_failed) { + Genode::warning("Packet dropped"); + } + + if (_verbose) + Genode::log(prefix, Genode::Cstring(string.string(), string.size()-2)); + + return string.size(); + } + + +}; diff --git a/src/proxy/udp_log/main.cc b/src/proxy/udp_log/main.cc new file mode 100644 index 0000000..7a7cdb4 --- /dev/null +++ b/src/proxy/udp_log/main.cc @@ -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 +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +#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 +{ + public: + typedef Genode::String Prefix; + + private: + + Genode::Env &_env; + Logger &_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 &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 +{ + private: + + Genode::Env &_env; + Genode::Allocator &_alloc; + Attached_rom_dataspace _config = { _env, "config" }; + Logger + _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(&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); + } +} diff --git a/src/proxy/udp_log/target.mk b/src/proxy/udp_log/target.mk new file mode 100644 index 0000000..062dabc --- /dev/null +++ b/src/proxy/udp_log/target.mk @@ -0,0 +1,3 @@ +TARGET = udp_log +SRC_CC = main.cc +LIBS = base net