diff --git a/repos/libports/run/lwip_lx.run b/repos/libports/run/lwip_lx.run index f0382fb30d..a0581f6b39 100644 --- a/repos/libports/run/lwip_lx.run +++ b/repos/libports/run/lwip_lx.run @@ -67,7 +67,6 @@ set config { - diff --git a/repos/os/recipes/raw/drivers_nic-linux/drivers.config b/repos/os/recipes/raw/drivers_nic-linux/drivers.config index f6f3275425..52b60259ba 100644 --- a/repos/os/recipes/raw/drivers_nic-linux/drivers.config +++ b/repos/os/recipes/raw/drivers_nic-linux/drivers.config @@ -16,7 +16,6 @@ - diff --git a/repos/os/src/drivers/nic/spec/linux/README b/repos/os/src/drivers/nic/spec/linux/README new file mode 100644 index 0000000000..bf073b13c6 --- /dev/null +++ b/repos/os/src/drivers/nic/spec/linux/README @@ -0,0 +1,20 @@ +This directory contains the implementation of the Linux TAP based NIC driver. + +The driver is a Linux hybrid component that uses the C library to +access the virtual TUN/TAP network devices. Therefore it requires +read-write access to /dev/net/tun and a TAP device configured to be +controlled by the user executing the component. The TAP device +defaults to tap0 and can be set up as follows. + +! sudo ip tuntap add dev tap0 mode tap user $USER + +The used TAP device can be changed in the component configuration like +follows. + +! + +The virtual NIC's MAC address defaults to 02-00-00-00-00-01 and can be +changed via + +! + diff --git a/repos/os/src/drivers/nic/spec/linux/main.cc b/repos/os/src/drivers/nic/spec/linux/main.cc index f309065cf4..8e197cb567 100644 --- a/repos/os/src/drivers/nic/spec/linux/main.cc +++ b/repos/os/src/drivers/nic/spec/linux/main.cc @@ -3,21 +3,12 @@ * \author Stefan Kalkowski * \author Christian Helmuth * \author Sebastian Sumpf + * \author Martin Stein * \date 2011-08-08 - * - * Configuration options are: - * - * - TAP device to connect to (default is tap0) - * - MAC address (default is 02-00-00-00-00-01) - * - * These can be set in the config section as follows: - * - * - * */ /* - * Copyright (C) 2011-2017 Genode Labs GmbH + * Copyright (C) 2011-2021 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. @@ -30,11 +21,9 @@ #include #include #include -#include /* NIC driver includes */ #include -#include /* Linux */ #include @@ -44,39 +33,25 @@ #include #include -namespace Server { - using namespace Genode; - struct Main; -} +using namespace Genode; + +using Tap_name = String; +using Mac_address = Net::Mac_address; -static Net::Mac_address default_mac_address() -{ - /* fall back to fake MAC address (unicast, locally managed) */ - Nic::Mac_address mac_addr { }; - mac_addr.addr[0] = 0x02; - mac_addr.addr[1] = 0x00; - mac_addr.addr[2] = 0x00; - mac_addr.addr[3] = 0x00; - mac_addr.addr[4] = 0x00; - mac_addr.addr[5] = 0x01; - return mac_addr; -} - - -class Linux_session_component : public Nic::Session_component +class Uplink_client : public Uplink_client_base { private: - struct Rx_signal_thread : Genode::Thread + struct Rx_signal_thread : Thread { - int fd; - Genode::Signal_context_capability sigh; - Genode::Blockade blockade { }; + int fd; + Uplink_client &uplink; + Blockade blockade { }; - Rx_signal_thread(Genode::Env &env, int fd, Genode::Signal_context_capability sigh) - : Genode::Thread(env, "rx_signal", 0x1000), fd(fd), sigh(sigh) { } + Rx_signal_thread(Env &env, int fd, Uplink_client &uplink) + : Thread(env, "rx_signal", 0x1000), fd(fd), uplink(uplink) { } void entry() override { @@ -90,20 +65,18 @@ class Linux_session_component : public Nic::Session_component do { ret = select(fd + 1, &rfds, 0, 0, 0); } while (ret < 0); /* signal incoming packet */ - Genode::Signal_transmitter(sigh).submit(); + uplink._rx_handler.local_submit(); blockade.block(); } } }; - Genode::Attached_rom_dataspace _config_rom; + int _tap_fd; + Signal_handler _rx_handler { _env.ep(), *this, &Uplink_client::_handle_rx }; + Rx_signal_thread _rx_thread { _env, _tap_fd, *this }; - Nic::Mac_address _mac_addr { }; - int _tap_fd; - Rx_signal_thread _rx_thread; - - int _setup_tap_fd() + static int _init_tap_fd(Tap_name const &tap_name) { /* open TAP device */ int ret; @@ -111,261 +84,33 @@ class Linux_session_component : public Nic::Session_component int fd = open("/dev/net/tun", O_RDWR); if (fd < 0) { - Genode::error("could not open /dev/net/tun: no virtual network emulation"); - /* this error is fatal */ - throw Genode::Exception(); + error("could not open /dev/net/tun: no virtual network emulation"); + throw Exception(); } /* set fd to non-blocking */ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { - Genode::error("could not set /dev/net/tun to non-blocking"); - throw Genode::Exception(); + error("could not set /dev/net/tun to non-blocking"); + throw Exception(); } - Genode::memset(&ifr, 0, sizeof(ifr)); + ::memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; - /* get tap device from config */ - try { - Genode::Xml_node nic_node = _config_rom.xml().sub_node("nic"); - nic_node.attribute("tap").with_raw_value([&] (char const *ptr, size_t len) { - len = Genode::min(len, sizeof(ifr.ifr_name) - 1); - Genode::memcpy(ifr.ifr_name, ptr, len); - ifr.ifr_name[len] = 0; - }); - Genode::log("using tap device \"", Genode::Cstring(ifr.ifr_name), "\""); - } catch (...) { - /* use tap0 if no config has been provided */ - Genode::copy_cstring(ifr.ifr_name, "tap0", sizeof(ifr.ifr_name)); - Genode::log("no config provided, using tap0"); - } + copy_cstring(ifr.ifr_name, tap_name.string(), sizeof(ifr.ifr_name)); + log("using tap device \"", tap_name, "\""); ret = ioctl(fd, TUNSETIFF, (void *) &ifr); if (ret != 0) { - Genode::error("could not configure /dev/net/tun: no virtual network emulation"); + error("could not configure /dev/net/tun: no virtual network emulation"); close(fd); /* this error is fatal */ - throw Genode::Exception(); + throw Exception(); } return fd; } - bool _send() - { - using namespace Genode; - - if (!_tx.sink()->ready_to_ack()) - return false; - - if (!_tx.sink()->packet_avail()) - return false; - - Packet_descriptor packet = _tx.sink()->get_packet(); - if (!packet.size() || !_tx.sink()->packet_valid(packet)) { - warning("invalid tx packet"); - return true; - } - - int ret; - - /* non-blocking-write packet to TAP */ - do { - ret = write(_tap_fd, _tx.sink()->packet_content(packet), packet.size()); - /* drop packet if write would block */ - if (ret < 0 && errno == EAGAIN) - continue; - - if (ret < 0) Genode::error("write: errno=", errno); - } while (ret < 0); - - _tx.sink()->acknowledge_packet(packet); - - return true; - } - - enum class Receive_result { - NO_PACKET, READ_ERROR, SUBMITTED, ALLOC_FAILED, SUBMIT_QUEUE_FULL }; - - Receive_result _receive() - { - unsigned const max_size = Nic::Packet_allocator::DEFAULT_PACKET_SIZE; - - if (!_rx.source()->ready_to_submit()) - return Receive_result::SUBMIT_QUEUE_FULL; - - Nic::Packet_descriptor p; - try { - p = _rx.source()->alloc_packet(max_size); - } catch (Session::Rx::Source::Packet_alloc_failed) { return Receive_result::ALLOC_FAILED; } - - int size = read(_tap_fd, _rx.source()->packet_content(p), max_size); - if (size <= 0) { - _rx.source()->release_packet(p); - return errno == EAGAIN ? Receive_result::NO_PACKET - : Receive_result::READ_ERROR; - } - - /* adjust packet size */ - Nic::Packet_descriptor p_adjust(p.offset(), size); - _rx.source()->submit_packet(p_adjust); - - return Receive_result::SUBMITTED; - } - - protected: - - bool _handle_incoming_packets() - { - while (true) { - switch (_receive()) { - case Receive_result::NO_PACKET: return true; - case Receive_result::READ_ERROR: return true; - case Receive_result::SUBMITTED: continue; - case Receive_result::ALLOC_FAILED: return false; - case Receive_result::SUBMIT_QUEUE_FULL: return false; - } - } - } - - void _handle_packet_stream() override - { - while (_rx.source()->ack_avail()) { - _rx.source()->release_packet(_rx.source()->get_acked_packet()); - } - - while (_send()) ; - - if (_handle_incoming_packets()) - _rx_thread.blockade.wakeup(); - } - - public: - - Linux_session_component(Genode::size_t const tx_buf_size, - Genode::size_t const rx_buf_size, - Genode::Allocator &rx_block_md_alloc, - Server::Env &env) - : - Session_component(tx_buf_size, rx_buf_size, Genode::CACHED, rx_block_md_alloc, env), - _config_rom(env, "config"), - _tap_fd(_setup_tap_fd()), - _rx_thread(env, _tap_fd, _packet_stream_dispatcher) - { - _mac_addr = default_mac_address(); - - /* try using configured MAC address */ - try { - Genode::Xml_node nic_config = _config_rom.xml().sub_node("nic"); - _mac_addr = nic_config.attribute_value("mac", _mac_addr); - Genode::log("Using configured MAC address ", _mac_addr); - } catch (...) { } - - _rx_thread.start(); - } - - bool link_state() override { return true; } - Nic::Mac_address mac_address() override { return _mac_addr; } -}; - - -class Uplink_client : public Genode::Uplink_client_base -{ - private: - - struct Rx_signal_thread : Genode::Thread - { - int fd; - Genode::Signal_context_capability sigh; - Genode::Blockade blockade { }; - - Rx_signal_thread(Genode::Env &env, int fd, Genode::Signal_context_capability sigh) - : Genode::Thread(env, "rx_signal", 0x1000), fd(fd), sigh(sigh) { } - - void entry() override - { - while (true) { - /* wait for packet arrival on fd */ - int ret; - fd_set rfds; - - FD_ZERO(&rfds); - FD_SET(fd, &rfds); - do { ret = select(fd + 1, &rfds, 0, 0, 0); } while (ret < 0); - - /* signal incoming packet */ - Genode::Signal_transmitter(sigh).submit(); - - blockade.block(); - } - } - }; - - int _tap_fd; - Genode::Signal_handler _rx_handler { _env.ep(), *this, &Uplink_client::_handle_rx }; - Rx_signal_thread _rx_thread { _env, _tap_fd, _rx_handler }; - - static int _init_tap_fd(Genode::Xml_node const &config) - { - /* open TAP device */ - int ret; - struct ifreq ifr; - - int fd = open("/dev/net/tun", O_RDWR); - if (fd < 0) { - Genode::error("could not open /dev/net/tun: no virtual network emulation"); - throw Genode::Exception(); - } - - /* set fd to non-blocking */ - if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { - Genode::error("could not set /dev/net/tun to non-blocking"); - throw Genode::Exception(); - } - - Genode::memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_flags = IFF_TAP | IFF_NO_PI; - - /* get tap device from config */ - try { - Genode::Xml_node nic_node = config.sub_node("nic"); - nic_node.attribute("tap").with_raw_value([&] (char const *ptr, size_t len) { - len = Genode::min(len, sizeof(ifr.ifr_name) - 1); - Genode::memcpy(ifr.ifr_name, ptr, len); - ifr.ifr_name[len] = 0; - }); - Genode::log("using tap device \"", Genode::Cstring(ifr.ifr_name), "\""); - } catch (...) { - /* use tap0 if no config has been provided */ - Genode::copy_cstring(ifr.ifr_name, "tap0", sizeof(ifr.ifr_name)); - Genode::log("no config provided, using tap0"); - } - - ret = ioctl(fd, TUNSETIFF, (void *) &ifr); - if (ret != 0) { - Genode::error("could not configure /dev/net/tun: no virtual network emulation"); - close(fd); - /* this error is fatal */ - throw Genode::Exception(); - } - - return fd; - } - - static Net::Mac_address - _init_mac_address(Genode::Xml_node const &config) - { - try { - return - config.sub_node("nic"). - attribute_value("mac", default_mac_address()); - - } catch (...) { - - return default_mac_address(); - } - } - void _handle_rx() { bool progress { true }; @@ -408,12 +153,12 @@ class Uplink_client : public Genode::Uplink_client_base /* non-blocking-write packet to TAP */ do { - ret = write(_tap_fd, conn_rx_pkt_base, conn_rx_pkt_size); + ret = ::write(_tap_fd, conn_rx_pkt_base, conn_rx_pkt_size); /* drop packet if write would block */ if (ret < 0 && errno == EAGAIN) continue; - if (ret < 0) Genode::error("write: errno=", errno); + if (ret < 0) error("write: errno=", errno); } while (ret < 0); return Transmit_result::ACCEPTED; @@ -421,14 +166,13 @@ class Uplink_client : public Genode::Uplink_client_base public: - Uplink_client(Genode::Env &env, - Genode::Allocator &alloc, - Genode::Xml_node const &config) + Uplink_client(Env &env, + Allocator &alloc, + Tap_name const &tap_name, + Mac_address const &mac_address) : - Uplink_client_base { - env, alloc, _init_mac_address(config) }, - - _tap_fd { _init_tap_fd(config) } + Uplink_client_base { env, alloc, mac_address }, + _tap_fd { _init_tap_fd(tap_name) } { _drv_handle_link_state(true); _rx_thread.start(); @@ -436,32 +180,32 @@ class Uplink_client : public Genode::Uplink_client_base }; -struct Server::Main +struct Main { - Env &_env; - Heap _heap { _env.ram(), _env.rm() }; - Genode::Attached_rom_dataspace _config_rom { _env, "config" }; + Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + Attached_rom_dataspace _config_rom { _env, "config" }; - Main(Env &env) : _env(env) + Tap_name _tap_name { + _config_rom.xml().attribute_value("tap", Tap_name("tap0")) }; + + static Mac_address _default_mac_address() { - Nic_driver_mode const mode { - read_nic_driver_mode(_config_rom.xml()) }; + Mac_address mac_addr { (uint8_t)0 }; - switch (mode) { - case Nic_driver_mode::NIC_SERVER: - { - Nic::Root &nic_root { - *new (_heap) Nic::Root(_env, _heap) }; + mac_addr.addr[0] = 0x02; /* unicast, locally managed MAC address */ + mac_addr.addr[5] = 0x01; /* arbitrary index */ - _env.parent().announce(_env.ep().manage(nic_root)); - break; - } - case Nic_driver_mode::UPLINK_CLIENT: - - new (_heap) Uplink_client(_env, _heap, _config_rom.xml()); - break; - } + return mac_addr; } + + Mac_address _mac_address { + _config_rom.xml().attribute_value("mac", _default_mac_address()) }; + + Uplink_client _uplink { _env, _heap, _tap_name, _mac_address }; + + Main(Env &env) : _env(env) { } }; -void Component::construct(Genode::Env &env) { static Server::Main main(env); } + +void Component::construct(Env &env) { static Main main(env); }