mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-01 15:10:56 +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
|
DIRS += include/libc/net
|
||||||
DIR_CONTENT(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)
|
radix.h route.h ethernet.h if_arp.h vnet.h)
|
||||||
|
|
||||||
DIRS += include/libc/netinet
|
DIRS += include/libc/netinet
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
base
|
base
|
||||||
|
net
|
||||||
os
|
os
|
||||||
so
|
so
|
||||||
timer_session
|
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 *);
|
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'
|
* Call functor 'fn' with ioctl info for the given file descriptor 'fd'
|
||||||
*
|
*
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <base/env.h>
|
#include <base/env.h>
|
||||||
#include <base/log.h>
|
#include <base/log.h>
|
||||||
#include <vfs/dir_file_system.h>
|
#include <vfs/dir_file_system.h>
|
||||||
|
#include <net/mac_address.h>
|
||||||
|
|
||||||
/* libc includes */
|
/* libc includes */
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@ -32,6 +33,8 @@
|
|||||||
#include <sys/disk.h>
|
#include <sys/disk.h>
|
||||||
#include <sys/soundcard.h>
|
#include <sys/soundcard.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_tap.h>
|
||||||
|
|
||||||
/* libc plugin interface */
|
/* libc plugin interface */
|
||||||
#include <libc-plugin/plugin.h>
|
#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)
|
int Libc::Vfs_plugin::ioctl(File_descriptor *fd, unsigned long request, char *argp)
|
||||||
{
|
{
|
||||||
Ioctl_result result { false, 0 };
|
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:
|
case SNDCTL_SYSINFO:
|
||||||
result = _ioctl_sndctl(fd, request, argp);
|
result = _ioctl_sndctl(fd, request, argp);
|
||||||
break;
|
break;
|
||||||
|
case TAPSIFINFO:
|
||||||
|
case TAPGIFINFO:
|
||||||
|
case TAPSDEBUG:
|
||||||
|
case TAPGDEBUG:
|
||||||
|
case TAPGIFNAME:
|
||||||
|
case SIOCGIFADDR:
|
||||||
|
case SIOCSIFADDR:
|
||||||
|
result = _ioctl_tapctl(fd, request, argp);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
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…
x
Reference in New Issue
Block a user