From 5627797e77a7895ab091e96d40f62668bdc31179 Mon Sep 17 00:00:00 2001 From: Johannes Schlatow Date: Mon, 5 Nov 2018 11:55:35 +0100 Subject: [PATCH] log_udp: receive LOG messages via UDP --- run/log_udp.run | 73 ++++++++++ src/proxy/log_udp/README | 13 ++ src/proxy/log_udp/main.cc | 54 +++++++ src/proxy/log_udp/receiver.h | 270 +++++++++++++++++++++++++++++++++++ src/proxy/log_udp/target.mk | 3 + 5 files changed, 413 insertions(+) create mode 100644 run/log_udp.run create mode 100644 src/proxy/log_udp/README create mode 100644 src/proxy/log_udp/main.cc create mode 100644 src/proxy/log_udp/receiver.h create mode 100644 src/proxy/log_udp/target.mk diff --git a/run/log_udp.run b/run/log_udp.run new file mode 100644 index 0000000..ebf7b60 --- /dev/null +++ b/run/log_udp.run @@ -0,0 +1,73 @@ +proc nic_drv_opt {} { + if {[have_spec linux]} { + return "ld=\"no\"" + } + return "" +} + +# +# Test logging to file system +# + +set build_components { + core init timer + proxy/log_udp + 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 log_udp + } [nic_drv_binary] { +} + +append_platform_drv_boot_modules +build_boot_image $boot_modules + +append qemu_args " -nographic" +append qemu_args " -net tap,ifname=tap1 " + +run_genode_until forever diff --git a/src/proxy/log_udp/README b/src/proxy/log_udp/README new file mode 100644 index 0000000..d96458f --- /dev/null +++ b/src/proxy/log_udp/README @@ -0,0 +1,13 @@ +log_udp is a LOG client that receives log messages as UDP +packets and forwards the messages to a local LOG session. + +By default, this component listens on UDP port 9 and +forwards every message (irrespective of IP addresses). +It also responds to ARP messages that are directed to +its IP address. + +! +! +! +! +! diff --git a/src/proxy/log_udp/main.cc b/src/proxy/log_udp/main.cc new file mode 100644 index 0000000..7e48402 --- /dev/null +++ b/src/proxy/log_udp/main.cc @@ -0,0 +1,54 @@ +/* + * \brief Receive LOG messages via UDP and forward to LOG session. + * \author Johannes Schlatow + * \date 2018-11-02 + */ + +/* + * 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 "receiver.h" + +#include +#include + + +using namespace Net; + +namespace Log_udp { + using Genode::size_t; + using Genode::Attached_rom_dataspace; + using Genode::Xml_node; + + struct Main; +}; + +struct Log_udp::Main +{ + Genode::Env &env; + Genode::Heap heap { &env.ram(), &env.rm() }; + Attached_rom_dataspace config { env, "config" }; + Receiver receiver { env, heap, config.xml() }; + + + Main(Genode::Env &env) : env(env) + { } + +}; + +namespace Component { + Genode::size_t stack_size() { return 2*1024*sizeof(long); } + void construct(Genode::Env &env) + { + env.exec_static_constructors(); + static Log_udp::Main main(env); + } +} diff --git a/src/proxy/log_udp/receiver.h b/src/proxy/log_udp/receiver.h new file mode 100644 index 0000000..987e555 --- /dev/null +++ b/src/proxy/log_udp/receiver.h @@ -0,0 +1,270 @@ +/* + * \brief UDP log receiver + * \author Johannes Schlatow + * \date 2018-11-02 + */ + +/* + * 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 + +using namespace Net; + +namespace Log_udp { + using Genode::size_t; + using Genode::Xml_node; + using Nic::Packet_stream_sink; + using Nic::Packet_stream_source; + using Nic::Packet_descriptor; + + class Receiver; +}; + +class Log_udp::Receiver +{ + private: + enum { + PACKET_SIZE = 512, + BUF_SIZE = Nic::Session::QUEUE_SIZE * PACKET_SIZE, + }; + + Nic::Packet_allocator _tx_block_alloc; + Nic::Connection _nic; + + Mac_address const _mac { _nic.mac_address() }; + Ipv4_address const _default_ip { (Genode::uint8_t)0x00 }; + Ipv4_address const _ip; + Port const _default_port { 9 }; + Port const _port; + bool _verbose { false }; + + Genode::Signal_handler _sink_ack; + Genode::Signal_handler _sink_submit; + Genode::Signal_handler _source_ack; + Genode::Signal_handler _source_submit; + + /** + * submit queue not empty anymore + */ + void _ready_to_submit(); + + /** + * acknowledgement queue not full anymore + * + * we assume ACK and SUBMIT queue to be equally + * dimensioned so that this signal can be ignored. + */ + void _ack_avail() { } + + /** + * 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_sink< ::Nic::Session::Policy> * sink() { + return _nic.rx(); } + + Packet_stream_source< ::Nic::Session::Policy> * source() { + return _nic.tx(); } + + public: + Receiver(Genode::Env &env, Genode::Allocator &alloc, Xml_node config) + : + _tx_block_alloc(&alloc), + _nic(env, &_tx_block_alloc, BUF_SIZE, BUF_SIZE), + _ip (config.attribute_value("ip", _default_ip)), + _port (config.attribute_value("port", _default_port)), + _verbose(config.attribute_value("verbose", _verbose)), + _sink_ack (env.ep(), *this, &Receiver::_ack_avail), + _sink_submit (env.ep(), *this, &Receiver::_ready_to_submit), + _source_ack (env.ep(), *this, &Receiver::_ready_to_ack), + _source_submit(env.ep(), *this, &Receiver::_packet_avail) + { + _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); + } + + /** + * Handle an ethernet packet + * + * \param src ethernet frame's address + * \param size ethernet frame's size. + */ + void handle_ethernet(void* src, Genode::size_t size); + + /* + * Handle an ARP packet + * + * \param eth ethernet frame containing the ARP packet. + * \param size size guard + */ + void handle_arp(Ethernet_frame ð, + Size_guard &size_guard); + + /* + * Handle an IP packet + * + * \param eth ethernet frame containing the IP packet. + * \param size size guard + */ + void handle_ip(Ethernet_frame ð, + Size_guard &size_guard); + + /* + * Handle a LOG message packet + * + * \param udp UDP packet containing the LOG message. + * \param size size guard + */ + void handle_message(Udp_packet &udp, + Size_guard &size_guard); + + /** + * Send ethernet frame + * + * \param eth ethernet frame to send. + * \param size ethernet frame's size. + */ + void send(Ethernet_frame *eth, Genode::size_t size); + +}; + +void Log_udp::Receiver::_ready_to_submit() +{ + Packet_descriptor _packet { }; + + /* as long as packets are available, and we can ack them */ + while (sink()->packet_avail()) { + _packet = sink()->get_packet(); + if (_packet.size()) { + handle_ethernet(sink()->packet_content(_packet), _packet.size()); + } + + if (!sink()->ready_to_ack()) { + Genode::warning("ack state FULL"); + return; + } + + sink()->acknowledge_packet(_packet); + } +} + +void Log_udp::Receiver::handle_ethernet(void* src, Genode::size_t size) +{ + try { + /* parse ethernet frame header */ + Size_guard size_guard(size); + Ethernet_frame ð = Ethernet_frame::cast_from(src, size_guard); + 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(Size_guard::Exceeded) { + Genode::warning("Packet size guard exceeded!"); + } +} + +void Log_udp::Receiver::handle_arp(Ethernet_frame ð, + Size_guard &size_guard) +{ + /* ignore broken packets */ + Arp_packet &arp = eth.data(size_guard); + if (!arp.ethernet_ipv4()) + return; + + /* look whether the IP address is our's */ + if (arp.dst_ip() == _ip) { + if (arp.opcode() == Arp_packet::REQUEST) { + /* + * The ARP packet gets re-written, we interchange source + * and destination MAC and IP addresses, and set the opcode + * to reply, and then push the packet back to the NIC driver. + */ + Ipv4_address old_src_ip = arp.src_ip(); + arp.opcode(Arp_packet::REPLY); + arp.dst_mac(arp.src_mac()); + arp.src_mac(_mac); + arp.src_ip(arp.dst_ip()); + arp.dst_ip(old_src_ip); + eth.dst(arp.dst_mac()); + + /* set our MAC as sender */ + eth.src(_mac); + send(ð, size_guard.total_size()); + } + } +} + +void Log_udp::Receiver::handle_ip(Ethernet_frame ð, + Size_guard &size_guard) +{ + Ipv4_packet &ip = eth.data(size_guard); + if (ip.protocol() == Ipv4_packet::Protocol::UDP) { + + Udp_packet &udp = ip.data(size_guard); + if (udp.dst_port() == Port(9)) + handle_message(udp, size_guard); + } +} + +void Log_udp::Receiver::handle_message(Udp_packet &udp, + Size_guard &size_guard) +{ + char *msg = &udp.data(size_guard); + + /* consume until '\0' */ + size_t len = 0; + for (char* s=msg; s && *s; s++, len++) { + if (len) + size_guard.consume_head(1); + } + + if (len) + Genode::log(Genode::Cstring(msg, len-1)); +} + +void Log_udp::Receiver::send(Ethernet_frame *eth, Genode::size_t size) +{ + try { + /* copy and submit packet */ + Packet_descriptor packet = source()->alloc_packet(size); + char *content = source()->packet_content(packet); + Genode::memcpy((void*)content, (void*)eth, size); + source()->submit_packet(packet); + } catch(Packet_stream_source< ::Nic::Session::Policy>::Packet_alloc_failed) { + Genode::warning("Packet dropped"); + } +} diff --git a/src/proxy/log_udp/target.mk b/src/proxy/log_udp/target.mk new file mode 100644 index 0000000..4b49f1b --- /dev/null +++ b/src/proxy/log_udp/target.mk @@ -0,0 +1,3 @@ +TARGET = log_udp +SRC_CC = main.cc +LIBS = base net