Files
genode-world/src/lib/remote_rom/backend/nic_ip/base.h
2019-05-07 10:14:34 +02:00

292 lines
7.9 KiB
C++

/*
* \brief Common base for client and server
* \author Johannes Schlatow
* \author Edgard Schmidt
* \date 2016-02-18
*/
#include <net/ethernet.h>
#include <net/arp.h>
#include <net/ipv4.h>
#include <net/udp.h>
#include <net/port.h>
#include <base/env.h>
#include <base/log.h>
#include <base/attached_rom_dataspace.h>
#include <nic/packet_allocator.h>
#include <nic_session/connection.h>
#include <timer_session/connection.h>
#include <packet.h>
#ifndef __INCLUDE__REMOTE_ROM__BASE_H_
#define __INCLUDE__REMOTE_ROM__BASE_H_
namespace Remote_rom {
using Genode::Packet_descriptor;
using Net::Ethernet_frame;
using Net::Ipv4_packet;
using Net::Mac_address;
using Net::Ipv4_address;
using Net::Size_guard;
using Net::Arp_packet;
using Net::Udp_packet;
using Net::Port;
class Backend_base;
};
class Remote_rom::Backend_base : public Genode::Interface
{
private:
Genode::Signal_handler<Backend_base> _link_state_handler;
Genode::Signal_handler<Backend_base> _rx_packet_handler;
Port _udp_port;
Port const _src_port { 51234 };
/* rx packet handler */
void _handle_rx_packet();
void _handle_arp_request(Ethernet_frame &eth,
Size_guard &guard,
Arp_packet &arp);
void _handle_link_state()
{
Genode::log("link state changed");
}
void _tx_ack(bool block = false)
{
/* check for acknowledgements */
while (_nic.tx()->ack_avail() || block) {
Nic::Packet_descriptor acked_packet = _nic.tx()->get_acked_packet();
_nic.tx()->release_packet(acked_packet);
block = false;
}
}
protected:
enum {
PACKET_SIZE = 1600,
BUF_SIZE = Nic::Session::QUEUE_SIZE * PACKET_SIZE
};
Timer::Connection _timer;
const bool _verbose = false;
bool _arp_waiting { false };
Nic::Packet_allocator _tx_block_alloc;
Nic::Connection _nic;
Mac_address _mac_address;
Mac_address _dst_mac;
Ipv4_address _src_ip;
Ipv4_address _accept_ip;
Ipv4_address _dst_ip;
bool _chksum_offload { false };
/**
* Handle accepted network packet from the other side
*/
virtual void receive(Packet &packet, Size_guard &) = 0;
inline void arp_request()
{
if (_dst_mac == Ethernet_frame::broadcast() && !_arp_waiting) {
size_t const frame_size = sizeof(Ethernet_frame)
+ sizeof(Arp_packet);
Nic::Packet_descriptor pd = alloc_tx_packet(frame_size);
Size_guard size_guard(pd.size());
char *content = _nic.tx()->packet_content(pd);
Ethernet_frame &eth = Ethernet_frame::construct_at(content,
size_guard);
eth.src(_mac_address);
eth.dst(Ethernet_frame::broadcast());
eth.type(Ethernet_frame::Type::ARP);
Arp_packet &arp = eth.construct_at_data<Arp_packet>(size_guard);
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(_mac_address);
arp.src_ip(_src_ip);
arp.dst_mac(Ethernet_frame::broadcast());
arp.dst_ip(_dst_ip);
_arp_waiting = true;
submit_tx_packet(pd);
}
}
Ethernet_frame &prepare_eth(void *base, Size_guard &size_guard)
{
arp_request();
Ethernet_frame &eth = Ethernet_frame::construct_at(base, size_guard);
eth.src(_mac_address);
eth.dst(_dst_mac);
eth.type(Ethernet_frame::Type::IPV4);
return eth;
}
Ipv4_packet &prepare_ipv4(Ethernet_frame &eth, Size_guard &size_guard)
{
Ipv4_packet &ip = eth.construct_at_data<Ipv4_packet>(size_guard);
ip.version(4);
ip.header_length(sizeof(Ipv4_packet) / 4);
ip.time_to_live(10);
ip.protocol(Ipv4_packet::Protocol::UDP);
ip.src(_src_ip);
ip.dst(_dst_ip);
return ip;
}
Udp_packet &prepare_udp(Ipv4_packet &ip, Size_guard &size_guard)
{
Udp_packet &udp = ip.construct_at_data<Udp_packet>(size_guard);
udp.src_port(_src_port);
udp.dst_port(_udp_port);
return udp;
}
template <typename T>
void transmit_notification(Packet::Type type,
T const &frontend)
{
size_t const frame_size = sizeof(Ethernet_frame)
+ sizeof(Ipv4_packet)
+ sizeof(Udp_packet)
+ sizeof(Packet)
+ sizeof(NotificationPacket);
Nic::Packet_descriptor pd = alloc_tx_packet(frame_size);
Size_guard size_guard(pd.size());
char *content = _nic.tx()->packet_content(pd);
Ethernet_frame &eth = prepare_eth(content, size_guard);
size_t const ip_off = size_guard.head_size();
Ipv4_packet &ip = prepare_ipv4(eth, size_guard);
size_t const udp_off = size_guard.head_size();
Udp_packet &udp = prepare_udp(ip, size_guard);
Packet &pak = udp.construct_at_data<Packet>(size_guard);
pak.type(type);
pak.module_name(frontend.module_name());
pak.content_hash(frontend.content_hash());
NotificationPacket &npak =
pak.construct_at_data<NotificationPacket>(size_guard);
npak.content_size(frontend.content_size());
/* 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();
submit_tx_packet(pd);
}
explicit Backend_base(Genode::Env &env,
Genode::Allocator &alloc,
Genode::Xml_node config,
Genode::Xml_node policy)
:
_link_state_handler(env.ep(), *this, &Backend_base::_handle_link_state),
_rx_packet_handler(env.ep(), *this, &Backend_base::_handle_rx_packet),
_udp_port(policy.attribute_value("udp_port", Port(9009))),
_timer(env),
_tx_block_alloc(&alloc),
_nic(env, &_tx_block_alloc, BUF_SIZE, BUF_SIZE),
_mac_address(_nic.mac_address()),
_dst_mac (policy.attribute_value("dst_mac", Ethernet_frame::broadcast())),
_src_ip (policy.attribute_value("src", Ipv4_packet::current())),
_accept_ip(policy.attribute_value("src", Ipv4_packet::broadcast())),
_dst_ip (policy.attribute_value("dst", Ipv4_packet::broadcast())),
_chksum_offload(config.attribute_value("chksum_offload", _chksum_offload))
{
_nic.link_state_sigh(_link_state_handler);
_nic.rx_channel()->sigh_packet_avail(_rx_packet_handler);
}
Nic::Packet_descriptor alloc_tx_packet(Genode::size_t size)
{
while (true) {
try {
Nic::Packet_descriptor packet = _nic.tx()->alloc_packet(size);
return packet;
} catch(Nic::Session::Tx::Source::Packet_alloc_failed) {
/* packet allocator exhausted, wait for acknowledgements */
_tx_ack(true);
}
}
}
void submit_tx_packet(Nic::Packet_descriptor packet)
{
/* check for acknowledgements */
_tx_ack();
if (!_nic.tx()->ready_to_submit()) {
Genode::error("not ready to submit");
return;
}
_nic.tx()->submit_packet(packet);
}
/**
* 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 &eth,
Size_guard &edguard);
/*
* Handle an IP packet
*
* \param eth Ethernet frame containing the IP packet.
* \param size size guard
*/
void handle_ip(Ethernet_frame &eth,
Size_guard &edguard);
/**
* Send ethernet frame
*
* \param eth ethernet frame to send.
* \param size ethernet frame's size.
*/
void send(Ethernet_frame *eth, Genode::size_t size);
};
#endif