如何与 Boost.Asio 同时从 TAP 设备读取(通过 posix::stream_descriptor)?

问题描述 投票:0回答:1

我的程序应该同时从生成的 TAP 设备读取数据包并处理它们。为此,我使用 LaKabanetuntap 库以及 Boost.Asio 的

posix::stream_descriptor
。 但是,由于我充当客户端而不是服务器,因此无法选择异步接受数据包。 我选择的临时解决方案是一次又一次异步读取。然而,这有两个主要问题:

  1. 可能会出现堆栈溢出,因为同一个函数经常被“无限”调用。 该函数接受数据包的速度不够快。我已经用
  2. sudo ping -f ff02::1%test
  3. 测试过了。
    
    
  4. 以下是我到目前为止的代码:

#include <iostream> #include <cstdlib> #include <boost/asio.hpp> #include <unistd.h> #include "tun_tap.hpp" void handle_packet([[maybe_unused]] const boost::system::error_code& error, [[maybe_unused]] std::size_t bytes_transferred, [[maybe_unused]] const std::array<char, 1520>& buffer) { if (error) { std::clog << "Error in handle_packet: " << error.message() << std::endl; return; } std::clog << "Received packet of size: " << bytes_transferred << std::endl; std::clog << std::flush; // To something with the packet sleep(5); } void start(boost::asio::posix::stream_descriptor& tap_device) { std::array<char, 1520> buffer; tap_device.async_read_some(boost::asio::buffer(buffer), [&](const boost::system::error_code& error, std::size_t bytes_transferred) { start(tap_device); handle_packet(error, bytes_transferred, buffer); }); } int main() { try { boost::asio::io_context io; const ::size_t mtu = 1500; std::clog << "Create TUN device." << std::endl; tun_tap dev = tun_tap("test", tun_tap_mode::tap); std::clog << "Set MTU to " << mtu << "." << std::endl; dev.set_mtu(1500); std::clog << "Set the TUN device up." << std::endl; dev.up(); boost::asio::posix::stream_descriptor tap_device(io, ::dup(dev.native_handler())); start(tap_device); io.run(); } catch (const std::exception &e) { std::cerr << "Error: " << e.what() << std::endl << "Exit program."; ::exit(EXIT_FAILURE); } return EXIT_SUCCESS; }

完整源码

我现在的问题是,如何使用 Boost.Asio 从 TAP 设备读取数据而不丢失数据包?

c++ networking boost boost-asio tun-tap
1个回答
0
投票

std::array<char, 1520> buffer; tap_device.async_read_some( // asio::buffer(buffer), [&](boost::system::error_code const& error, std::size_t bytes_transferred) { start(tap_device); handle_packet(error, bytes_transferred, buffer); });

首先,这
不是

递归。完成处理程序是一个“延续”。根据定义,它是在异步操作完成时调用的。所以新的 start 永远不会重叠。此外,它不在启动函数的堆栈帧中执行。相反,它在服务线程上执行

然而,这也是问题所在。 buffer 是局部变量。它的生命周期在 

async_read_some

启动后立即结束,因此

根据定义
在它完成之前。因此,当它在完成处理程序中使用时,它就变得无效了。
因此,由于代码已被破坏,让我们忽略有关速度的有缺陷的观察。首先,让我们修复它。有多种方法,但在我看来,这种方法最具指导性/可扩展性:
struct Client {
    using Handler = std::function<void(error_code, std::string)>;
    Client(asio::any_io_executor ex, tun_tap& dev, Handler handler = default_handler)
        : tap_device_(ex, dev.native_handle())
        , handler_(std::move(handler)) {
        start();
    }

  private:
    stream_descriptor      tap_device_;
    Handler                handler_;
    std::array<char, 1520> buffer_{};

    void start() {
        tap_device_.async_read_some( //
            asio::buffer(buffer_),   //
            [this](error_code const& error, size_t bytes_transferred) {
                if (error) {
                    std::cerr << "Error: " << error.message() << std::endl;
                } else {
                    std::string packet(buffer_.data(), bytes_transferred);
                    start();

                    if (handler_) {
                        handler_(error, std::move(packet));
                    }
                };
            });
    }

    static void default_handler(error_code ec, std::string const& packet) {
        if (ec) {
            std::cerr << "Error: " << ec.message() << std::endl;
        } else {
            std::cout << "Received packet: " << packet.size() << " bytes" << std::endl;
        }
    }
};

int main() try {
    constexpr uint16_t mtu = 1500;
    asio::io_context io;

    tun_tap dev{"test", tun_tap_mode::tap};
    dev.set_mtu(mtu);
    dev.up();

    Client c(io.get_executor(), dev);

    io.run();
} catch (std::exception const& e) {
    std::cerr << "Error: " << e.what() << std::endl << "Exit program.";
    return 1;
}

我还审查了 tun_tap.hpp/cpp 修复了一些问题和泄漏:

文件

tun_tap.hpp
  • #ifndef TUNTAP_HPP #define TUNTAP_HPP #include <cstdint> #include <string> #include <tuntap.h> #include <memory> enum class tun_tap_mode { tun = 0, tap }; class tun_tap { public: tun_tap(std::string const& ifname, tun_tap_mode const& mode); void set_ip(std::string const& ip, uint8_t netmask); void set_mtu(uint16_t mtu); void up(); void down(); int native_handle(); private: struct Destroy final { constexpr void operator()(device* dev) const noexcept { if (dev) ::tuntap_destroy(dev); } }; std::unique_ptr<device, Destroy> _device; }; #endif

    
    

    文件
    tun_tap.cpp
  • #include "tun_tap.hpp" #include <linux/if.h> #include <stdexcept> tun_tap::tun_tap(std::string const& ifname, tun_tap_mode const& mode) { if (ifname.empty()) throw std::invalid_argument("ifname"); if (ifname.size() > IFNAMSIZ) throw std::invalid_argument("ifname"); if (mode != tun_tap_mode::tun && mode != tun_tap_mode::tap) throw std::invalid_argument("tun_tap_mode"); _device.reset(tuntap_init()); int m = mode == tun_tap_mode::tun ? TUNTAP_MODE_TUNNEL : TUNTAP_MODE_ETHERNET; if (::tuntap_start(_device.get(), m, TUNTAP_ID_ANY)) throw std::runtime_error("Failed to start tuntap device."); if (::tuntap_set_ifname(_device.get(), ifname.c_str())) throw std::runtime_error("Failed to set ifname for tuntap device."); } void tun_tap::up() { if (::tuntap_up(_device.get())) throw std::runtime_error("Failed to bring tuntap device up."); } void tun_tap::down() { if (::tuntap_down(_device.get())) throw std::runtime_error("Failed to bring tuntap device down."); } void tun_tap::set_mtu(uint16_t mtu) { if (::tuntap_set_mtu(_device.get(), mtu)) throw std::runtime_error("Failed to set mtu for tuntap device."); } void tun_tap::set_ip(std::string const& ip, uint8_t netmask) { if (netmask > 128) // TODO FIXME? seems 32 should be the max due to ipv4 throw std::invalid_argument("netmask"); if (::tuntap_set_ip(_device.get(), ip.c_str(), netmask)) throw std::runtime_error("Failed to set ip address for tuntap device."); } int tun_tap::native_handle() { // return ::tuntap_get_fd(_device.get()); }

    
    

    这里效果很好:

© www.soinside.com 2019 - 2024. All rights reserved.