mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-21 22:47:50 +00:00
vfs_tap: VFS plugin for Uplink/Nic session access
This plugin emulates a `/dev/tapX` device as found on FreeBSD. See README for more information. genodelabs/genode#4394
This commit is contained in:
parent
19958eafcf
commit
019cacf07e
@ -1 +1 @@
|
||||
392389f9e1323249c044ba6b7fea6ea3d73100c5
|
||||
bfc0a252597735c423dff56b699b816d4fbda7e6
|
||||
|
@ -96,7 +96,7 @@ DIR_CONTENT(include/libc/vm) := \
|
||||
|
||||
DIRS += include/libc/net
|
||||
DIR_CONTENT(include/libc/net) := \
|
||||
$(addprefix $(D)/sys/net/, if.h if_dl.h if_tun.h if_types.h \
|
||||
$(addprefix $(D)/sys/net/, if.h if_dl.h if_tun.h if_tap.h if_types.h \
|
||||
radix.h route.h ethernet.h if_arp.h vnet.h)
|
||||
|
||||
DIRS += include/libc/netinet
|
||||
|
@ -1,4 +1,5 @@
|
||||
base
|
||||
net
|
||||
os
|
||||
so
|
||||
timer_session
|
||||
|
143
repos/libports/run/libc_vfs_tap.run
Normal file
143
repos/libports/run/libc_vfs_tap.run
Normal file
@ -0,0 +1,143 @@
|
||||
#
|
||||
# Build
|
||||
#
|
||||
#
|
||||
|
||||
create_boot_directory
|
||||
|
||||
import_from_depot [depot_user]/src/[base_src] \
|
||||
[depot_user]/src/vfs_tap
|
||||
|
||||
set build_components {
|
||||
core init timer
|
||||
server/nic_router
|
||||
test/libc_vfs_tap
|
||||
}
|
||||
|
||||
build $build_components
|
||||
|
||||
#
|
||||
# Generate config
|
||||
#
|
||||
|
||||
append config {
|
||||
<config verbose="yes">
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="PD"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="LOG"/>
|
||||
</parent-provides>
|
||||
<default-route>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
<default caps="100"/>
|
||||
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides> <service name="Timer"/> </provides>
|
||||
</start>
|
||||
|
||||
<start name="nic_router">
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<provides>
|
||||
<service name="Nic"/>
|
||||
<service name="Uplink"/>
|
||||
</provides>
|
||||
<config verbose="yes" verbose_packets="yes" verbose_domain_state="yes">
|
||||
<policy label_prefix="tap_uplink_client -> " domain="tap_uplink"/>
|
||||
<policy label_prefix="tap_nic_client -> " domain="tap_nic"/>
|
||||
|
||||
<domain name="tap_nic">
|
||||
</domain>
|
||||
|
||||
<domain name="tap_uplink">
|
||||
</domain>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="tap_uplink_client">
|
||||
<binary name="test-libc_vfs_tap"/>
|
||||
<resource name="RAM" quantum="8M"/>
|
||||
<config>
|
||||
<libc stdin="/dev/log" stdout="/dev/log" stderr="/dev/log"/>
|
||||
<vfs>
|
||||
<dir name="dev">
|
||||
<log/>
|
||||
<tap name="tap0" mac="02:02:00:00:00:20" mode="uplink_client"/>
|
||||
</dir>
|
||||
</vfs>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="tap_nic_client">
|
||||
<binary name="test-libc_vfs_tap"/>
|
||||
<resource name="RAM" quantum="8M"/>
|
||||
<config>
|
||||
<libc stdin="/dev/log" stdout="/dev/log" stderr="/dev/log"/>
|
||||
<vfs>
|
||||
<dir name="dev">
|
||||
<log/>
|
||||
<tap name="tap0"/>
|
||||
</dir>
|
||||
</vfs>
|
||||
</config>
|
||||
</start>
|
||||
</config>
|
||||
}
|
||||
|
||||
install_config $config
|
||||
|
||||
#
|
||||
# Boot modules
|
||||
#
|
||||
|
||||
|
||||
set boot_modules {
|
||||
core init timer test-libc_vfs_tap nic_router
|
||||
libc.lib.so vfs.lib.so libm.lib.so posix.lib.so
|
||||
}
|
||||
|
||||
build_boot_image $boot_modules
|
||||
|
||||
append qemu_args "-nographic "
|
||||
|
||||
run_genode_until "child \"tap_uplink_client\" exited with exit value 0" 40
|
||||
|
||||
set original_output $output
|
||||
|
||||
grep_output {\[init -> tap_uplink_client\].*}
|
||||
compare_output_to {
|
||||
[init -> tap_uplink_client] MAC address 02:02:00:00:00:20
|
||||
[init -> tap_uplink_client] Successfully opened device tap0
|
||||
[init -> tap_uplink_client] MAC address 02:02:00:00:00:21
|
||||
[init -> tap_uplink_client] Warning: unsupported ioctl (request=0x4008745c)
|
||||
[init -> tap_uplink_client] Warning: TAPGIFINFO failed
|
||||
}
|
||||
|
||||
set output $original_output
|
||||
grep_output {\[init -> tap_nic_client\].*}
|
||||
compare_output_to {
|
||||
[init -> tap_nic_client] Successfully opened device tap0
|
||||
[init -> tap_nic_client] Warning: unsupported ioctl (request=0x4008745c)
|
||||
[init -> tap_nic_client] Warning: TAPGIFINFO failed
|
||||
}
|
||||
|
||||
# check that nic_router received packages from both clients
|
||||
set output $original_output
|
||||
grep_output {\[init -> nic_router\] \[tap.*}
|
||||
|
||||
set num_uplink_received [regexp -all {.*tap_uplink\] rcv} $output dummy]
|
||||
if {$num_uplink_received < 1} {
|
||||
puts "Error: No packet received from tap_uplink_client\n"
|
||||
exit 1
|
||||
}
|
||||
|
||||
set num_nic_received [regexp -all {.*tap_nic\] rcv} $output dummy]
|
||||
if {$num_nic_received < 1} {
|
||||
puts "Error: No packet received from tap_nic_client\n"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# vi: set ft=tcl :
|
@ -126,6 +126,11 @@ class Libc::Vfs_plugin final : public Plugin
|
||||
*/
|
||||
Ioctl_result _ioctl_sndctl(File_descriptor *, unsigned long, char *);
|
||||
|
||||
/**
|
||||
* Tap related I/O controls
|
||||
*/
|
||||
Ioctl_result _ioctl_tapctl(File_descriptor *, unsigned long, char *);
|
||||
|
||||
/**
|
||||
* Call functor 'fn' with ioctl info for the given file descriptor 'fd'
|
||||
*
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <base/env.h>
|
||||
#include <base/log.h>
|
||||
#include <vfs/dir_file_system.h>
|
||||
#include <net/mac_address.h>
|
||||
|
||||
/* libc includes */
|
||||
#include <errno.h>
|
||||
@ -32,6 +33,8 @@
|
||||
#include <sys/disk.h>
|
||||
#include <sys/soundcard.h>
|
||||
#include <dlfcn.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_tap.h>
|
||||
|
||||
/* libc plugin interface */
|
||||
#include <libc-plugin/plugin.h>
|
||||
@ -1770,6 +1773,85 @@ Libc::Vfs_plugin::_ioctl_sndctl(File_descriptor *fd, unsigned long request, char
|
||||
}
|
||||
|
||||
|
||||
Libc::Vfs_plugin::Ioctl_result
|
||||
Libc::Vfs_plugin::_ioctl_tapctl(File_descriptor *fd, unsigned long request, char *argp)
|
||||
{
|
||||
bool handled = false;
|
||||
int result = 0;
|
||||
|
||||
if (request == TAPGIFNAME) { /* return device name */
|
||||
if (!argp)
|
||||
return { true, EINVAL };
|
||||
|
||||
ifreq *ifr = reinterpret_cast<ifreq*>(argp);
|
||||
|
||||
monitor().monitor([&] {
|
||||
_with_info(*fd, [&] (Xml_node info) {
|
||||
if (info.type() == "tap") {
|
||||
String<IFNAMSIZ> name = info.attribute_value("name", String<IFNAMSIZ> { });
|
||||
copy_cstring(ifr->ifr_name, name.string(), IFNAMSIZ);
|
||||
handled = true;
|
||||
}
|
||||
});
|
||||
|
||||
return Fn::COMPLETE;
|
||||
});
|
||||
}
|
||||
else if (request == SIOCGIFADDR) { /* get MAC address */
|
||||
if (!argp)
|
||||
return { true, EINVAL };
|
||||
|
||||
monitor().monitor([&] {
|
||||
_with_info(*fd, [&] (Xml_node info) {
|
||||
if (info.type() == "tap") {
|
||||
Net::Mac_address mac = info.attribute_value("mac_addr", Net::Mac_address { });
|
||||
mac.copy(argp);
|
||||
handled = true;
|
||||
}
|
||||
});
|
||||
|
||||
return Fn::COMPLETE;
|
||||
});
|
||||
}
|
||||
else if (request == SIOCSIFADDR) { /* set MAC address */
|
||||
if (!argp)
|
||||
return { true, EINVAL };
|
||||
|
||||
Net::Mac_address new_mac { argp };
|
||||
String<18> mac_string { new_mac };
|
||||
|
||||
/* write string into file */
|
||||
Absolute_path mac_addr_path = ioctl_dir(*fd);
|
||||
mac_addr_path.append_element("mac_addr");
|
||||
File_descriptor *mac_addr_fd = open(mac_addr_path.base(), O_RDWR);
|
||||
if (!mac_addr_fd)
|
||||
return { true, ENOTSUP };
|
||||
write(mac_addr_fd, mac_string.string(), mac_string.length());
|
||||
close(mac_addr_fd);
|
||||
|
||||
monitor().monitor([&] {
|
||||
/* check whether mac address changed, return ENOTSUP if not */
|
||||
_with_info(*fd, [&] (Xml_node info) {
|
||||
if (info.type() == "tap") {
|
||||
if (!info.has_attribute("mac_addr"))
|
||||
result = ENOTSUP;
|
||||
else {
|
||||
Net::Mac_address cur_mac = info.attribute_value("mac_addr", Net::Mac_address { });
|
||||
if (cur_mac != new_mac)
|
||||
result = ENOTSUP;
|
||||
}
|
||||
|
||||
handled = true;
|
||||
}
|
||||
});
|
||||
|
||||
return Fn::COMPLETE;
|
||||
});
|
||||
}
|
||||
|
||||
return { handled, result };
|
||||
}
|
||||
|
||||
int Libc::Vfs_plugin::ioctl(File_descriptor *fd, unsigned long request, char *argp)
|
||||
{
|
||||
Ioctl_result result { false, 0 };
|
||||
@ -1807,6 +1889,15 @@ int Libc::Vfs_plugin::ioctl(File_descriptor *fd, unsigned long request, char *ar
|
||||
case SNDCTL_SYSINFO:
|
||||
result = _ioctl_sndctl(fd, request, argp);
|
||||
break;
|
||||
case TAPSIFINFO:
|
||||
case TAPGIFINFO:
|
||||
case TAPSDEBUG:
|
||||
case TAPGDEBUG:
|
||||
case TAPGIFNAME:
|
||||
case SIOCGIFADDR:
|
||||
case SIOCSIFADDR:
|
||||
result = _ioctl_tapctl(fd, request, argp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
93
repos/libports/src/test/libc_vfs_tap/main.c
Normal file
93
repos/libports/src/test/libc_vfs_tap/main.c
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* \brief tap device loopback test using FreeBSD API
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-01-26
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_tap.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
enum { BUFFLEN = 1500 };
|
||||
|
||||
int fd = open("/dev/tap0", O_RDWR);
|
||||
if (fd == -1) {
|
||||
printf("Error: open(/dev/tap0) failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct ifreq ifr;
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
if (ioctl(fd, TAPGIFNAME, (void *)&ifr) < 0) {
|
||||
printf("Error: TAPGIFNAME failed\n");
|
||||
return 2;
|
||||
}
|
||||
printf("Successfully opened device %s\n", ifr.ifr_name);
|
||||
|
||||
/* get mac address */
|
||||
char mac[6];
|
||||
memset(mac, 0, sizeof(mac));
|
||||
if (ioctl(fd, SIOCGIFADDR, (void *)mac) < 0) {
|
||||
printf("Error: SIOCGIFADDR failed\n");
|
||||
return 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mac address if we are in uplink mode.
|
||||
* In Uplink mode, the default mac address is 0x02 02 02 02 02 02.
|
||||
* In Nic mode, the nic_router will assign 0x02 02 02 02 02 00 to the first
|
||||
* client.
|
||||
*/
|
||||
if (mac[5] >= 0x02) {
|
||||
mac[5]++;
|
||||
if (ioctl(fd, SIOCSIFADDR, (void *)mac) < 0) {
|
||||
printf("Error: SIOCSIFADDR failed\n");
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
/* try other ioctls */
|
||||
struct tapinfo info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
if (ioctl(fd, TAPGIFINFO, (void *)&info) < 0)
|
||||
printf("Warning: TAPGIFINFO failed\n");
|
||||
|
||||
char buffer[BUFFLEN];
|
||||
unsigned frame_cnt = 0;
|
||||
while (frame_cnt < 2) {
|
||||
/* read a frame */
|
||||
ssize_t received = read(fd, buffer, BUFFLEN);
|
||||
if (received < 0)
|
||||
return 1;
|
||||
|
||||
/* write a frame */
|
||||
ssize_t written = write(fd, buffer, received);
|
||||
if (written < received) {
|
||||
printf("Unable to write frame %d\n", frame_cnt);
|
||||
return 1;
|
||||
}
|
||||
frame_cnt++;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
5
repos/libports/src/test/libc_vfs_tap/target.mk
Normal file
5
repos/libports/src/test/libc_vfs_tap/target.mk
Normal file
@ -0,0 +1,5 @@
|
||||
TARGET = test-libc_vfs_tap
|
||||
|
||||
LIBS := posix
|
||||
|
||||
SRC_C := main.c
|
5
repos/os/lib/mk/vfs_tap.mk
Normal file
5
repos/os/lib/mk/vfs_tap.mk
Normal file
@ -0,0 +1,5 @@
|
||||
SRC_CC := vfs_tap.cc
|
||||
|
||||
vpath %.cc $(REP_DIR)/src/lib/vfs/tap
|
||||
|
||||
SHARED_LIB := yes
|
9
repos/os/recipes/src/vfs_tap/content.mk
Normal file
9
repos/os/recipes/src/vfs_tap/content.mk
Normal file
@ -0,0 +1,9 @@
|
||||
MIRROR_FROM_REP_DIR := src/lib/vfs/tap lib/mk/vfs_tap.mk
|
||||
|
||||
content: $(MIRROR_FROM_REP_DIR) LICENSE
|
||||
|
||||
$(MIRROR_FROM_REP_DIR):
|
||||
$(mirror_from_rep_dir)
|
||||
|
||||
LICENSE:
|
||||
cp $(GENODE_DIR)/LICENSE $@
|
1
repos/os/recipes/src/vfs_tap/hash
Normal file
1
repos/os/recipes/src/vfs_tap/hash
Normal file
@ -0,0 +1 @@
|
||||
2022-01-27 06d96f2618f11d62f9a959da196753337dbb4c4c
|
5
repos/os/recipes/src/vfs_tap/used_apis
Normal file
5
repos/os/recipes/src/vfs_tap/used_apis
Normal file
@ -0,0 +1,5 @@
|
||||
base
|
||||
nic_session
|
||||
os
|
||||
uplink_session
|
||||
vfs
|
46
repos/os/src/lib/vfs/tap/README
Normal file
46
repos/os/src/lib/vfs/tap/README
Normal file
@ -0,0 +1,46 @@
|
||||
The VFS TAP plugin offers access to Genode's Uplink or Nic session by providing
|
||||
a special file system. It exposes a data file that reflects a _/dev/tap0_
|
||||
file. The support of I/O control operations is provided in form of a structured
|
||||
'info' file located in the directory named after the data file, e.g.
|
||||
_/dev/.tap0/info_.
|
||||
|
||||
This file may by used to query the configured parameters and has the following
|
||||
structure:
|
||||
|
||||
! <tap name="tap0" mac_addr="..."/>
|
||||
|
||||
Each parameter can also be accessed via its own file. The following list
|
||||
presents all files:
|
||||
|
||||
* :mac_addr (rw): The MAC address of the device (immutable when in Nic mode).
|
||||
* :name (ro): The name of the device.
|
||||
|
||||
When mounting the tap file system, the following optional attributes may
|
||||
be provided:
|
||||
|
||||
* :mode: If set to "uplink_client", the plugin will open an Uplink session
|
||||
instead of a Nic session.
|
||||
* :label: Sets the session label of the Uplink/Nic session. If not provided,
|
||||
an empty label is used.
|
||||
* :mac: Sets the default mac address when mode="uplink_client".
|
||||
|
||||
The following config snippet illustrates its configuration:
|
||||
|
||||
! <vfs>
|
||||
! <dir name="dev">
|
||||
! <tap name="tap0"/>
|
||||
! </dir>
|
||||
! </vfs>
|
||||
|
||||
Note, that the plugin emulates the tap device and its I/O control operations
|
||||
as expected by FreeBSD's libc. On Linux, the tap devices are created by
|
||||
performing an I/O control operation on _/dev/net/tun_ after which the opened
|
||||
file descriptor can be used for reading/writing. If only a single tap device is
|
||||
needed, it is possible to use the tap plugin for _/dev/net/tun_ and just omit
|
||||
the ioctl by creating an emulation header file _linux/if_tun.h_ with the
|
||||
following content:
|
||||
|
||||
! #include <net/if_tap.h>
|
||||
! #define TUNSETIFF TAPGIFNAME
|
||||
! #define IFF_TAP 0
|
||||
! #define IFF_NO_PI 0
|
200
repos/os/src/lib/vfs/tap/nic_file_system.h
Normal file
200
repos/os/src/lib/vfs/tap/nic_file_system.h
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* \brief Vfs handle for a Nic client.
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-01-26
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__LIB__VFS__TAP__NIC_FILE_SYSTEM_H_
|
||||
#define _SRC__LIB__VFS__TAP__NIC_FILE_SYSTEM_H_
|
||||
|
||||
#include <net/mac_address.h>
|
||||
#include <nic/packet_allocator.h>
|
||||
#include <nic_session/connection.h>
|
||||
#include <vfs/single_file_system.h>
|
||||
|
||||
|
||||
namespace Vfs {
|
||||
using namespace Genode;
|
||||
|
||||
class Nic_file_system;
|
||||
}
|
||||
|
||||
|
||||
class Vfs::Nic_file_system : public Vfs::Single_file_system
|
||||
{
|
||||
public:
|
||||
|
||||
class Nic_vfs_handle;
|
||||
|
||||
using Vfs_handle = Nic_vfs_handle;
|
||||
|
||||
Nic_file_system(char const *name)
|
||||
: Single_file_system(Node_type::TRANSACTIONAL_FILE, name,
|
||||
Node_rwx::rw(), Genode::Xml_node("<data/>"))
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
class Vfs::Nic_file_system::Nic_vfs_handle : public Single_vfs_handle
|
||||
{
|
||||
public:
|
||||
|
||||
using Label = String<64>;
|
||||
|
||||
private:
|
||||
|
||||
using Read_result = File_io_service::Read_result;
|
||||
using Write_result = File_io_service::Write_result;
|
||||
|
||||
enum { PKT_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE };
|
||||
enum { BUF_SIZE = Uplink::Session::QUEUE_SIZE * PKT_SIZE };
|
||||
|
||||
Genode::Env &_env;
|
||||
Nic::Packet_allocator _pkt_alloc;
|
||||
Nic::Connection _nic;
|
||||
bool _link_state;
|
||||
|
||||
bool _notifying = false;
|
||||
bool _blocked = false;
|
||||
|
||||
Io_signal_handler<Nic_vfs_handle> _link_state_handler { _env.ep(), *this, &Nic_vfs_handle::_handle_link_state};
|
||||
Io_signal_handler<Nic_vfs_handle> _read_avail_handler { _env.ep(), *this, &Nic_vfs_handle::_handle_read_avail };
|
||||
Io_signal_handler<Nic_vfs_handle> _ack_avail_handler { _env.ep(), *this, &Nic_vfs_handle::_handle_ack_avail };
|
||||
|
||||
void _handle_ack_avail()
|
||||
{
|
||||
while (_nic.tx()->ack_avail()) {
|
||||
_nic.tx()->release_packet(_nic.tx()->get_acked_packet()); }
|
||||
}
|
||||
|
||||
void _handle_read_avail()
|
||||
{
|
||||
if (!read_ready())
|
||||
return;
|
||||
|
||||
if (_blocked) {
|
||||
_blocked = false;
|
||||
io_progress_response();
|
||||
}
|
||||
|
||||
if (_notifying) {
|
||||
_notifying = false;
|
||||
read_ready_response();
|
||||
}
|
||||
}
|
||||
|
||||
void _handle_link_state()
|
||||
{
|
||||
_link_state = _nic.link_state();
|
||||
_handle_read_avail();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Nic_vfs_handle(Genode::Env &env,
|
||||
Allocator &alloc,
|
||||
Label const &label,
|
||||
Net::Mac_address const &,
|
||||
Directory_service &ds,
|
||||
File_io_service &fs,
|
||||
int flags)
|
||||
: Single_vfs_handle { ds, fs, alloc, flags },
|
||||
_env(env),
|
||||
_pkt_alloc(&alloc),
|
||||
_nic(_env, &_pkt_alloc, BUF_SIZE, BUF_SIZE, label.string()),
|
||||
_link_state(_nic.link_state())
|
||||
{
|
||||
_nic.link_state_sigh(_link_state_handler);
|
||||
_nic.tx_channel()->sigh_ack_avail (_ack_avail_handler);
|
||||
_nic.rx_channel()->sigh_ready_to_ack (_read_avail_handler);
|
||||
_nic.rx_channel()->sigh_packet_avail (_read_avail_handler);
|
||||
}
|
||||
|
||||
bool notify_read_ready() override {
|
||||
_notifying = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void mac_address(Net::Mac_address const &) { }
|
||||
|
||||
Net::Mac_address mac_address() {
|
||||
return _nic.mac_address(); }
|
||||
|
||||
/************************
|
||||
* Vfs_handle interface *
|
||||
************************/
|
||||
|
||||
bool read_ready() override {
|
||||
return _link_state && _nic.rx()->packet_avail() && _nic.rx()->ready_to_ack(); }
|
||||
|
||||
Read_result read(char *dst, file_size count,
|
||||
file_size &out_count) override
|
||||
{
|
||||
if (!read_ready()) {
|
||||
_blocked = true;
|
||||
return Read_result::READ_QUEUED;
|
||||
}
|
||||
|
||||
out_count = 0;
|
||||
|
||||
/* process a single packet from rx stream */
|
||||
Packet_descriptor const rx_pkt { _nic.rx()->get_packet() };
|
||||
|
||||
if (rx_pkt.size() > 0 &&
|
||||
_nic.rx()->packet_valid(rx_pkt)) {
|
||||
|
||||
const char *const rx_pkt_base {
|
||||
_nic.rx()->packet_content(rx_pkt) };
|
||||
|
||||
out_count = static_cast<file_size>(min(rx_pkt.size(), static_cast<size_t>(count)));
|
||||
memcpy(dst, rx_pkt_base, static_cast<size_t>(out_count));
|
||||
|
||||
_nic.rx()->acknowledge_packet(rx_pkt);
|
||||
}
|
||||
|
||||
return Read_result::READ_OK;
|
||||
}
|
||||
|
||||
Write_result write(char const *src, file_size count,
|
||||
file_size &out_count) override
|
||||
{
|
||||
out_count = 0;
|
||||
|
||||
_handle_ack_avail();
|
||||
|
||||
if (!_nic.tx()->ready_to_submit()) {
|
||||
return Write_result::WRITE_ERR_WOULD_BLOCK;
|
||||
}
|
||||
try {
|
||||
size_t tx_pkt_size { static_cast<size_t>(count) };
|
||||
|
||||
Packet_descriptor tx_pkt {
|
||||
_nic.tx()->alloc_packet(tx_pkt_size) };
|
||||
|
||||
void *tx_pkt_base {
|
||||
_nic.tx()->packet_content(tx_pkt) };
|
||||
|
||||
memcpy(tx_pkt_base, src, tx_pkt_size);
|
||||
|
||||
_nic.tx()->submit_packet(tx_pkt);
|
||||
out_count = tx_pkt_size;
|
||||
|
||||
return Write_result::WRITE_OK;
|
||||
} catch (...) {
|
||||
|
||||
warning("exception while trying to forward packet from driver "
|
||||
"to Nic connection TX");
|
||||
|
||||
return Write_result::WRITE_ERR_INVALID;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _SRC__LIB__VFS__TAP__NIC_FILE_SYSTEM_H_ */
|
2
repos/os/src/lib/vfs/tap/target.mk
Normal file
2
repos/os/src/lib/vfs/tap/target.mk
Normal file
@ -0,0 +1,2 @@
|
||||
TARGET = dummy-vfs_tap
|
||||
LIBS = vfs_tap
|
232
repos/os/src/lib/vfs/tap/uplink_client_base.h
Normal file
232
repos/os/src/lib/vfs/tap/uplink_client_base.h
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* \brief Modified base class for the Uplink client role of NIC drivers
|
||||
* \author Martin Stein
|
||||
* \author Johannes Schlatow
|
||||
* \date 2020-12-07
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _DRIVERS__NIC__UPLINK_CLIENT_BASE_H_
|
||||
#define _DRIVERS__NIC__UPLINK_CLIENT_BASE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <net/mac_address.h>
|
||||
#include <nic/packet_allocator.h>
|
||||
#include <uplink_session/connection.h>
|
||||
|
||||
namespace Genode {
|
||||
|
||||
class Uplink_client_base;
|
||||
}
|
||||
|
||||
class Genode::Uplink_client_base : Noncopyable
|
||||
{
|
||||
public:
|
||||
|
||||
using Label = String<64>;
|
||||
|
||||
protected:
|
||||
|
||||
enum class Transmit_result { ACCEPTED, REJECTED, RETRY };
|
||||
|
||||
enum class Write_result { WRITE_SUCCEEDED, WRITE_FAILED };
|
||||
|
||||
enum { PKT_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE };
|
||||
enum { BUF_SIZE = Uplink::Session::QUEUE_SIZE * PKT_SIZE };
|
||||
|
||||
Env &_env;
|
||||
Allocator &_alloc;
|
||||
Label const &_label;
|
||||
Net::Mac_address _drv_mac_addr;
|
||||
bool _drv_mac_addr_used { false };
|
||||
bool _drv_link_state { false };
|
||||
Constructible<Uplink::Connection> _conn { };
|
||||
Nic::Packet_allocator _conn_pkt_alloc { &_alloc };
|
||||
Io_signal_handler<Uplink_client_base> _conn_rx_ready_to_ack_handler { _env.ep(), *this, &Uplink_client_base::_conn_rx_handle_ready_to_ack };
|
||||
Io_signal_handler<Uplink_client_base> _conn_rx_packet_avail_handler { _env.ep(), *this, &Uplink_client_base::_conn_rx_handle_packet_avail };
|
||||
Io_signal_handler<Uplink_client_base> _conn_tx_ack_avail_handler { _env.ep(), *this, &Uplink_client_base::_conn_tx_handle_ack_avail };
|
||||
|
||||
/*****************************************
|
||||
** Interface towards Uplink connection **
|
||||
*****************************************/
|
||||
|
||||
void _conn_rx_handle_ready_to_ack()
|
||||
{
|
||||
if (!_conn.constructed())
|
||||
return;
|
||||
|
||||
if (_custom_conn_rx_ready_to_ack_handler())
|
||||
_custom_conn_rx_handle_ready_to_ack();
|
||||
}
|
||||
|
||||
void _conn_tx_handle_ack_avail()
|
||||
{
|
||||
if (!_conn.constructed())
|
||||
return;
|
||||
|
||||
while (_conn->tx()->ack_avail()) {
|
||||
_conn->tx()->release_packet(_conn->tx()->get_acked_packet()); }
|
||||
}
|
||||
|
||||
void _conn_rx_handle_packet_avail()
|
||||
{
|
||||
if (!_conn.constructed())
|
||||
return;
|
||||
|
||||
if (_custom_conn_rx_packet_avail_handler())
|
||||
_custom_conn_rx_handle_packet_avail();
|
||||
}
|
||||
|
||||
|
||||
/***************************************************
|
||||
** Generic back end for interface towards driver **
|
||||
***************************************************/
|
||||
|
||||
template <typename FUNC>
|
||||
void _drv_rx_handle_pkt(size_t conn_tx_pkt_size,
|
||||
FUNC && write_to_conn_tx_pkt)
|
||||
{
|
||||
if (!_conn.constructed()) {
|
||||
return;
|
||||
}
|
||||
_conn_tx_handle_ack_avail();
|
||||
|
||||
if (!_conn->tx()->ready_to_submit()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Packet_descriptor conn_tx_pkt {
|
||||
_conn->tx()->alloc_packet(conn_tx_pkt_size) };
|
||||
|
||||
void *conn_tx_pkt_base {
|
||||
_conn->tx()->packet_content(conn_tx_pkt) };
|
||||
|
||||
size_t adjusted_conn_tx_pkt_size {
|
||||
conn_tx_pkt_size };
|
||||
|
||||
Write_result write_result {
|
||||
write_to_conn_tx_pkt(
|
||||
conn_tx_pkt_base,
|
||||
adjusted_conn_tx_pkt_size) };
|
||||
|
||||
switch (write_result) {
|
||||
case Write_result::WRITE_SUCCEEDED:
|
||||
|
||||
if (adjusted_conn_tx_pkt_size == conn_tx_pkt_size) {
|
||||
|
||||
_conn->tx()->submit_packet(conn_tx_pkt);
|
||||
|
||||
} else if (adjusted_conn_tx_pkt_size < conn_tx_pkt_size) {
|
||||
|
||||
Packet_descriptor adjusted_conn_tx_pkt {
|
||||
conn_tx_pkt.offset(), adjusted_conn_tx_pkt_size };
|
||||
|
||||
_conn->tx()->submit_packet(adjusted_conn_tx_pkt);
|
||||
|
||||
} else {
|
||||
|
||||
class Bad_size { };
|
||||
throw Bad_size { };
|
||||
}
|
||||
break;
|
||||
|
||||
case Write_result::WRITE_FAILED:
|
||||
|
||||
_conn->tx()->release_packet(conn_tx_pkt);
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
|
||||
warning("exception while trying to forward packet from driver "
|
||||
"to Uplink connection TX");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void _drv_handle_link_state(bool drv_link_state)
|
||||
{
|
||||
if (_drv_link_state == drv_link_state) {
|
||||
return;
|
||||
}
|
||||
_drv_link_state = drv_link_state;
|
||||
if (drv_link_state) {
|
||||
|
||||
/* create connection */
|
||||
_drv_mac_addr_used = true;
|
||||
_conn.construct(
|
||||
_env, &_conn_pkt_alloc, BUF_SIZE, BUF_SIZE,
|
||||
_drv_mac_addr, _label.string());
|
||||
|
||||
/* install signal handlers at connection */
|
||||
_conn->rx_channel()->sigh_ready_to_ack(
|
||||
_conn_rx_ready_to_ack_handler);
|
||||
|
||||
_conn->rx_channel()->sigh_packet_avail(
|
||||
_conn_rx_packet_avail_handler);
|
||||
|
||||
_conn->tx_channel()->sigh_ack_avail(
|
||||
_conn_tx_ack_avail_handler);
|
||||
|
||||
} else {
|
||||
|
||||
_conn.destruct();
|
||||
_drv_mac_addr_used = false;
|
||||
}
|
||||
}
|
||||
|
||||
virtual Transmit_result
|
||||
_drv_transmit_pkt(const char *conn_rx_pkt_base,
|
||||
size_t conn_rx_pkt_size) = 0;
|
||||
|
||||
virtual void _custom_conn_rx_handle_packet_avail()
|
||||
{
|
||||
class Unexpected_call { };
|
||||
throw Unexpected_call { };
|
||||
}
|
||||
|
||||
virtual void _custom_conn_rx_handle_ready_to_ack()
|
||||
{
|
||||
class Unexpected_call { };
|
||||
throw Unexpected_call { };
|
||||
}
|
||||
|
||||
virtual bool _custom_conn_rx_packet_avail_handler() { return false; }
|
||||
virtual bool _custom_conn_rx_ready_to_ack_handler() { return false; }
|
||||
|
||||
public:
|
||||
|
||||
Uplink_client_base(Env &env,
|
||||
Allocator &alloc,
|
||||
Net::Mac_address const &drv_mac_addr,
|
||||
Label const &label)
|
||||
:
|
||||
_env { env },
|
||||
_alloc { alloc },
|
||||
_label { label },
|
||||
_drv_mac_addr { drv_mac_addr }
|
||||
{
|
||||
log("MAC address ", _drv_mac_addr);
|
||||
}
|
||||
|
||||
void mac_address(Net::Mac_address const &mac_address)
|
||||
{
|
||||
if (_drv_mac_addr_used) {
|
||||
|
||||
class Already_in_use { };
|
||||
throw Already_in_use { };
|
||||
}
|
||||
_drv_mac_addr = mac_address;
|
||||
log("MAC address ", _drv_mac_addr);
|
||||
}
|
||||
|
||||
virtual ~Uplink_client_base() { }
|
||||
};
|
||||
|
||||
#endif /* _DRIVERS__NIC__UPLINK_CLIENT_BASE_H_ */
|
178
repos/os/src/lib/vfs/tap/uplink_file_system.h
Normal file
178
repos/os/src/lib/vfs/tap/uplink_file_system.h
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* \brief Vfs handle for an uplink client.
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-01-24
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__LIB__VFS__TAP__UPLINK_FILE_SYSTEM_H_
|
||||
#define _SRC__LIB__VFS__TAP__UPLINK_FILE_SYSTEM_H_
|
||||
|
||||
#include <vfs/single_file_system.h>
|
||||
|
||||
/* local Uplink_client_base with added custom handler */
|
||||
#include "uplink_client_base.h"
|
||||
|
||||
namespace Vfs {
|
||||
using namespace Genode;
|
||||
|
||||
class Uplink_file_system;
|
||||
}
|
||||
|
||||
|
||||
class Vfs::Uplink_file_system : public Vfs::Single_file_system
|
||||
{
|
||||
public:
|
||||
|
||||
class Uplink_vfs_handle;
|
||||
|
||||
using Vfs_handle = Uplink_vfs_handle;
|
||||
|
||||
Uplink_file_system(char const *name)
|
||||
: Single_file_system(Node_type::TRANSACTIONAL_FILE, name,
|
||||
Node_rwx::rw(), Genode::Xml_node("<data/>"))
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
class Vfs::Uplink_file_system::Uplink_vfs_handle : public Single_vfs_handle,
|
||||
public Genode::Uplink_client_base
|
||||
{
|
||||
private:
|
||||
|
||||
using Read_result = File_io_service::Read_result;
|
||||
using Write_result = File_io_service::Write_result;
|
||||
|
||||
bool _notifying = false;
|
||||
bool _blocked = false;
|
||||
|
||||
void _handle_read_avail()
|
||||
{
|
||||
if (!read_ready())
|
||||
return;
|
||||
|
||||
if (_blocked) {
|
||||
_blocked = false;
|
||||
io_progress_response();
|
||||
}
|
||||
|
||||
if (_notifying) {
|
||||
_notifying = false;
|
||||
read_ready_response();
|
||||
}
|
||||
}
|
||||
|
||||
/************************
|
||||
** Uplink_client_base **
|
||||
************************/
|
||||
|
||||
bool _custom_conn_rx_ready_to_ack_handler() override { return true; }
|
||||
bool _custom_conn_rx_packet_avail_handler() override { return true; }
|
||||
|
||||
void _custom_conn_rx_handle_packet_avail() override { _handle_read_avail(); }
|
||||
void _custom_conn_rx_handle_ready_to_ack() override { _handle_read_avail(); }
|
||||
|
||||
Transmit_result _drv_transmit_pkt(const char *, size_t) override
|
||||
{
|
||||
class Unexpected_call { };
|
||||
throw Unexpected_call { };
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Uplink_vfs_handle(Genode::Env &env,
|
||||
Allocator &alloc,
|
||||
Label const &label,
|
||||
Net::Mac_address const &mac,
|
||||
Directory_service &ds,
|
||||
File_io_service &fs,
|
||||
int flags)
|
||||
: Single_vfs_handle { ds, fs, alloc, flags },
|
||||
Uplink_client_base { env, alloc, mac, label }
|
||||
{ _drv_handle_link_state(true); }
|
||||
|
||||
bool notify_read_ready() override
|
||||
{
|
||||
_notifying = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void mac_address(Net::Mac_address const & mac)
|
||||
{
|
||||
if (_drv_mac_addr_used) {
|
||||
_drv_handle_link_state(false);
|
||||
Uplink_client_base::mac_address(mac);
|
||||
_drv_handle_link_state(true);
|
||||
}
|
||||
else
|
||||
Uplink_client_base::mac_address(mac);
|
||||
}
|
||||
|
||||
Net::Mac_address mac_address() const {
|
||||
return _drv_mac_addr; }
|
||||
|
||||
/************************
|
||||
* Vfs_handle interface *
|
||||
************************/
|
||||
|
||||
bool read_ready() override {
|
||||
return _drv_link_state && _conn->rx()->packet_avail() && _conn->rx()->ready_to_ack(); }
|
||||
|
||||
Read_result read(char *dst, file_size count,
|
||||
file_size &out_count) override
|
||||
{
|
||||
if (!_conn.constructed())
|
||||
return Read_result::READ_ERR_INVALID;
|
||||
|
||||
if (!read_ready()) {
|
||||
_blocked = true;
|
||||
return Read_result::READ_QUEUED;
|
||||
}
|
||||
|
||||
out_count = 0;
|
||||
|
||||
/* process a single packet from rx stream */
|
||||
Packet_descriptor const conn_rx_pkt { _conn->rx()->get_packet() };
|
||||
|
||||
if (conn_rx_pkt.size() > 0 &&
|
||||
_conn->rx()->packet_valid(conn_rx_pkt)) {
|
||||
|
||||
const char *const conn_rx_pkt_base {
|
||||
_conn->rx()->packet_content(conn_rx_pkt) };
|
||||
|
||||
out_count = static_cast<file_size>(min(conn_rx_pkt.size(), static_cast<size_t>(count)));
|
||||
memcpy(dst, conn_rx_pkt_base, static_cast<size_t>(out_count));
|
||||
|
||||
_conn->rx()->acknowledge_packet(conn_rx_pkt);
|
||||
}
|
||||
|
||||
return Read_result::READ_OK;
|
||||
}
|
||||
|
||||
Write_result write(char const *src, file_size count,
|
||||
file_size &out_count) override
|
||||
{
|
||||
if (!_conn.constructed())
|
||||
return Write_result::WRITE_ERR_INVALID;
|
||||
|
||||
out_count = 0;
|
||||
_drv_rx_handle_pkt(static_cast<size_t>(count), [&] (void * dst, size_t dst_size) {
|
||||
out_count = dst_size;
|
||||
memcpy(dst, src, dst_size);
|
||||
return Uplink_client_base::Write_result::WRITE_SUCCEEDED;
|
||||
});
|
||||
|
||||
if (out_count == count)
|
||||
return Write_result::WRITE_OK;
|
||||
else
|
||||
return Write_result::WRITE_ERR_WOULD_BLOCK;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _SRC__LIB__VFS__TAP__UPLINK_FILE_SYSTEM_H_ */
|
379
repos/os/src/lib/vfs/tap/vfs_tap.cc
Normal file
379
repos/os/src/lib/vfs/tap/vfs_tap.cc
Normal file
@ -0,0 +1,379 @@
|
||||
/*
|
||||
* \brief Tap device emulation
|
||||
* \author Johannes Schlatow
|
||||
* \date 2022-01-21
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <net/mac_address.h>
|
||||
#include <os/vfs.h>
|
||||
#include <vfs/single_file_system.h>
|
||||
#include <vfs/dir_file_system.h>
|
||||
#include <vfs/readonly_value_file_system.h>
|
||||
#include <vfs/value_file_system.h>
|
||||
#include <util/xml_generator.h>
|
||||
|
||||
/* local includes */
|
||||
#include "uplink_file_system.h"
|
||||
#include "nic_file_system.h"
|
||||
|
||||
namespace Vfs {
|
||||
enum Uplink_mode {
|
||||
NIC_CLIENT,
|
||||
UPLINK_CLIENT
|
||||
};
|
||||
static inline size_t ascii_to(char const *, Uplink_mode &);
|
||||
|
||||
/* overload Value_file_system to work with Net::Mac_address */
|
||||
class Mac_file_system;
|
||||
|
||||
/* main file system */
|
||||
class Tap_file_system;
|
||||
}
|
||||
|
||||
|
||||
class Vfs::Mac_file_system : public Value_file_system<Net::Mac_address>
|
||||
{
|
||||
public:
|
||||
|
||||
Mac_file_system(Name const & name, Net::Mac_address const & mac)
|
||||
: Value_file_system(name, mac)
|
||||
{ }
|
||||
|
||||
using Value_file_system<Net::Mac_address>::value;
|
||||
|
||||
Net::Mac_address value()
|
||||
{
|
||||
Net::Mac_address val { };
|
||||
Net::ascii_to(buffer().string(), val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
Net::Mac_address value() const
|
||||
{
|
||||
Net::Mac_address val { };
|
||||
Net::ascii_to(buffer().string(), val);
|
||||
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Vfs::Tap_file_system
|
||||
{
|
||||
using Name = String<64>;
|
||||
|
||||
template <typename>
|
||||
struct Local_factory;
|
||||
|
||||
template <typename>
|
||||
struct Data_file_system;
|
||||
|
||||
template <typename>
|
||||
struct Compound_file_system;
|
||||
|
||||
struct Device_update_handler;
|
||||
};
|
||||
|
||||
|
||||
Genode::size_t Vfs::ascii_to(char const *s, Uplink_mode &mode)
|
||||
{
|
||||
|
||||
if (!strcmp(s, "uplink", 6)) { mode = Uplink_mode::UPLINK_CLIENT; return 6; }
|
||||
if (!strcmp(s, "uplink_client", 13)) { mode = Uplink_mode::UPLINK_CLIENT; return 13; }
|
||||
|
||||
mode = Uplink_mode::NIC_CLIENT;
|
||||
return strlen(s);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interface for upwards reporting if the tap device state changed.
|
||||
* Currently, it is only used for triggering the info fs to read the
|
||||
* mac address from the device.
|
||||
*/
|
||||
struct Vfs::Tap_file_system::Device_update_handler : Interface
|
||||
{
|
||||
virtual void device_state_changed() = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* File system node for processing the packet data read/write
|
||||
*/
|
||||
template <typename FS>
|
||||
class Vfs::Tap_file_system::Data_file_system : public FS
|
||||
{
|
||||
private:
|
||||
|
||||
using Local_vfs_handle = typename FS::Vfs_handle;
|
||||
using Label = typename FS::Vfs_handle::Label;
|
||||
using Registered_handle = Genode::Registered<Local_vfs_handle>;
|
||||
using Handle_registry = Genode::Registry<Registered_handle>;
|
||||
using Open_result = Directory_service::Open_result;
|
||||
|
||||
Name const &_name;
|
||||
Label const &_label;
|
||||
Net::Mac_address const &_default_mac;
|
||||
Genode::Env &_env;
|
||||
Device_update_handler &_device_update_handler;
|
||||
Handle_registry _handle_registry { };
|
||||
|
||||
public:
|
||||
|
||||
Data_file_system(Genode::Env & env,
|
||||
Name const & name,
|
||||
Label const & label,
|
||||
Net::Mac_address const & mac,
|
||||
Device_update_handler & handler)
|
||||
:
|
||||
FS(name.string()),
|
||||
_name(name), _label(label), _default_mac(mac), _env(env),
|
||||
_device_update_handler(handler)
|
||||
{ }
|
||||
|
||||
/* must only be called if handle has been opened */
|
||||
Local_vfs_handle &device()
|
||||
{
|
||||
Local_vfs_handle *dev = nullptr;
|
||||
_handle_registry.for_each([&] (Local_vfs_handle &handle) {
|
||||
dev = &handle;
|
||||
});
|
||||
|
||||
struct Device_unavailable { };
|
||||
|
||||
if (!dev)
|
||||
throw Device_unavailable();
|
||||
|
||||
return *dev;
|
||||
}
|
||||
|
||||
static const char *name() { return "data"; }
|
||||
char const *type() override { return "data"; }
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory service interface **
|
||||
*********************************/
|
||||
|
||||
Open_result open(char const *path, unsigned flags,
|
||||
Vfs_handle **out_handle,
|
||||
Allocator &alloc) override
|
||||
{
|
||||
if (!FS::_single_file(path))
|
||||
return Open_result::OPEN_ERR_UNACCESSIBLE;
|
||||
|
||||
/* A tap device is exclusive open, thus return error if already opened. */
|
||||
unsigned handles = 0;
|
||||
_handle_registry.for_each([&handles] (Local_vfs_handle const &) {
|
||||
handles++;
|
||||
});
|
||||
if (handles) return Open_result::OPEN_ERR_EXISTS;
|
||||
|
||||
try {
|
||||
*out_handle = new (alloc)
|
||||
Registered_handle(_handle_registry, _env, alloc, _label.string(),
|
||||
_default_mac, *this, *this, flags);
|
||||
_device_update_handler.device_state_changed();
|
||||
return Open_result::OPEN_OK;
|
||||
}
|
||||
catch (Genode::Out_of_ram) { return Open_result::OPEN_ERR_OUT_OF_RAM; }
|
||||
catch (Genode::Out_of_caps) { return Open_result::OPEN_ERR_OUT_OF_CAPS; }
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
template <typename FS>
|
||||
struct Vfs::Tap_file_system::Local_factory : File_system_factory,
|
||||
Device_update_handler
|
||||
{
|
||||
using Label = typename FS::Vfs_handle::Label;
|
||||
using Name_fs = Readonly_value_file_system<Name>;
|
||||
using Mac_addr_fs = Mac_file_system;
|
||||
|
||||
Name const _name;
|
||||
Label const _label;
|
||||
Uplink_mode const _mode;
|
||||
Net::Mac_address const _default_mac;
|
||||
Vfs::Env &_env;
|
||||
|
||||
Data_file_system<FS> _data_fs { _env.env(), _name, _label, _default_mac, *this };
|
||||
|
||||
struct Info
|
||||
{
|
||||
Name const &_name;
|
||||
Mac_addr_fs const &_mac_addr_fs;
|
||||
|
||||
Info(Name const & name,
|
||||
Mac_addr_fs const & mac_addr_fs)
|
||||
: _name(name),
|
||||
_mac_addr_fs(mac_addr_fs)
|
||||
{ }
|
||||
|
||||
void print(Genode::Output &out) const
|
||||
{
|
||||
|
||||
char buf[128] { };
|
||||
Genode::Xml_generator xml(buf, sizeof(buf), "tap", [&] () {
|
||||
xml.attribute("mac_addr", String<20>(_mac_addr_fs.value()));
|
||||
xml.attribute("name", _name);
|
||||
});
|
||||
Genode::print(out, Genode::Cstring(buf));
|
||||
}
|
||||
};
|
||||
|
||||
Mac_addr_fs _mac_addr_fs { "mac_addr", _default_mac };
|
||||
Name_fs _name_fs { "name", _name };
|
||||
|
||||
Info _info { _name, _mac_addr_fs };
|
||||
Readonly_value_file_system<Info> _info_fs { "info", _info };
|
||||
|
||||
/********************
|
||||
** Watch handlers **
|
||||
********************/
|
||||
|
||||
Genode::Watch_handler<Vfs::Tap_file_system::Local_factory<FS>> _mac_addr_changed_handler {
|
||||
_mac_addr_fs, "/mac_addr",
|
||||
_env.alloc(),
|
||||
*this,
|
||||
&Vfs::Tap_file_system::Local_factory<FS>::_mac_addr_changed };
|
||||
|
||||
void _mac_addr_changed()
|
||||
{
|
||||
Net::Mac_address new_mac = _mac_addr_fs.value();
|
||||
|
||||
/* update of mac address only if changed */
|
||||
if (new_mac != _data_fs.device().mac_address())
|
||||
_data_fs.device().mac_address(new_mac);
|
||||
|
||||
/* read back mac from device */
|
||||
_mac_addr_fs.value(_data_fs.device().mac_address());
|
||||
|
||||
/* propagate changes to info_fs */
|
||||
_info_fs.value(_info);
|
||||
}
|
||||
|
||||
/*****************************
|
||||
** Device update interface **
|
||||
*****************************/
|
||||
|
||||
void device_state_changed() override
|
||||
{
|
||||
/* update mac address */
|
||||
_mac_addr_fs.value(_data_fs.device().mac_address());
|
||||
|
||||
/* propagate changes to info_fs */
|
||||
_info_fs.value(_info);
|
||||
}
|
||||
|
||||
/***********************
|
||||
** Factory interface **
|
||||
***********************/
|
||||
|
||||
Vfs::File_system *create(Vfs::Env&, Xml_node node) override
|
||||
{
|
||||
if (node.has_type("data")) return &_data_fs;
|
||||
if (node.has_type("info")) return &_info_fs;
|
||||
if (node.has_type("mac_addr")) return &_mac_addr_fs;
|
||||
if (node.has_type("name")) return &_name_fs;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/***********************
|
||||
** Constructor, etc. **
|
||||
***********************/
|
||||
|
||||
static Name name(Xml_node config)
|
||||
{
|
||||
return config.attribute_value("name", Name("tap"));
|
||||
}
|
||||
|
||||
Local_factory(Vfs::Env &env, Xml_node config)
|
||||
:
|
||||
_name (name(config)),
|
||||
_label (config.attribute_value("label", Label(""))),
|
||||
_mode (config.attribute_value("mode", Uplink_mode::NIC_CLIENT)),
|
||||
_default_mac(config.attribute_value("mac", Net::Mac_address { 0x02 })),
|
||||
_env(env)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
template <typename FS>
|
||||
class Vfs::Tap_file_system::Compound_file_system : private Local_factory<FS>,
|
||||
public Vfs::Dir_file_system
|
||||
{
|
||||
private:
|
||||
|
||||
typedef Tap_file_system::Name Name;
|
||||
|
||||
typedef String<200> Config;
|
||||
static Config _config(Name const &name)
|
||||
{
|
||||
char buf[Config::capacity()] { };
|
||||
|
||||
/*
|
||||
* By not using the node type "dir", we operate the
|
||||
* 'Dir_file_system' in root mode, allowing multiple sibling nodes
|
||||
* to be present at the mount point.
|
||||
*/
|
||||
Genode::Xml_generator xml(buf, sizeof(buf), "compound", [&] () {
|
||||
|
||||
xml.node("data", [&] () {
|
||||
xml.attribute("name", name); });
|
||||
|
||||
xml.node("dir", [&] () {
|
||||
xml.attribute("name", Name(".", name));
|
||||
xml.node("info", [&] () {});
|
||||
xml.node("mac_addr", [&] () {});
|
||||
xml.node("name", [&] () {});
|
||||
});
|
||||
});
|
||||
|
||||
return Config(Genode::Cstring(buf));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Compound_file_system(Vfs::Env &vfs_env, Genode::Xml_node node)
|
||||
:
|
||||
Local_factory<FS>(vfs_env, node),
|
||||
Vfs::Dir_file_system(vfs_env,
|
||||
Xml_node(_config(Local_factory<FS>::name(node)).string()),
|
||||
*this)
|
||||
{ }
|
||||
|
||||
static const char *name() { return "tap"; }
|
||||
|
||||
char const *type() override { return name(); }
|
||||
};
|
||||
|
||||
|
||||
extern "C" Vfs::File_system_factory *vfs_file_system_factory(void)
|
||||
{
|
||||
struct Factory : Vfs::File_system_factory
|
||||
{
|
||||
Vfs::File_system *create(Vfs::Env &env, Genode::Xml_node config) override
|
||||
{
|
||||
if (config.attribute_value("mode", Vfs::Uplink_mode::NIC_CLIENT) == Vfs::Uplink_mode::NIC_CLIENT)
|
||||
return new (env.alloc())
|
||||
Vfs::Tap_file_system::Compound_file_system<Vfs::Nic_file_system>(env, config);
|
||||
else
|
||||
return new (env.alloc())
|
||||
Vfs::Tap_file_system::Compound_file_system<Vfs::Uplink_file_system>(env, config);
|
||||
}
|
||||
};
|
||||
|
||||
static Factory f;
|
||||
return &f;
|
||||
}
|
Loading…
Reference in New Issue
Block a user