292 lines
7.9 KiB
C++
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 ð,
|
|
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 ð = 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 ð = 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 ð, 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 ð = 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 ð,
|
|
Size_guard &edguard);
|
|
|
|
/*
|
|
* Handle an IP packet
|
|
*
|
|
* \param eth Ethernet frame containing the IP packet.
|
|
* \param size size guard
|
|
*/
|
|
void handle_ip(Ethernet_frame ð,
|
|
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
|