内核 qdiscs 模块中的网络数据包缓冲

2023-12-04

我想缓冲源自容器网络接口的输出数据包。 这个netlink库文件名为sch_plug.chttps://code.woboq.org/linux/linux/net/sched/sch_plug.c.html看起来可以解决问题,但我发现它很难使用?我应该如何调用这些函数才能真正使其工作?如何获取参数,例如struct netlink_ext_ack *extack, struct sk_buff *skb等传递给源代码中定义的那些函数?


命令行

可以使用以下命令控制 qdiscnl-qdisc-add, nl-qdisc-delete, nl-qdisc-list(部分libnl). The --help标志可用于显示一些用法示例(link):

  • 为网络接口创建缓冲区大小为 32KB 的插件 qdisceth0:

    # nl-qdisc-add --dev=eth0 --parent=root plug --limit=32768
    
  • 默认情况下,插件 qdisc 将处于缓冲模式(意味着它阻止所有传出流量)。您可以使用以下命令在缓冲模式和释放模式之间切换:

    • 切换到释放模式:

      # nl-qdisc-add --dev=eth0 --parent=root --update plug --release-indefinite
      
    • 切换回缓冲模式:

      # nl-qdisc-add --dev=eth0 --parent=root --update plug --buffer
      
  • 您可以使用以下命令检查活动的 qdisc:

      # nl-qdisc-list --kind=plug --details --stats
    

    这还将告诉您每个 qdisc 的 id。

  • 根据 id,您可以再次删除 qdisc:

     # nl-qdisc-delete --id <id>
    

来自代码

可以检查上面使用的工具的代码来编写自定义实现(link):

#include <linux/netlink.h>

#include <netlink/netlink.h>
#include <netlink/route/qdisc.h>
#include <netlink/route/qdisc/plug.h>
#include <netlink/socket.h>

#include <atomic>
#include <csignal>
#include <iostream>
#include <stdexcept>

/**
 * Netlink route socket.
 */
struct Socket {
  Socket() : handle{nl_socket_alloc()} {

    if (handle == nullptr) {
      throw std::runtime_error{"Failed to allocate socket!"};
    }

    if (int err = nl_connect(handle, NETLINK_ROUTE); err < 0) {
      throw std::runtime_error{"Unable to connect netlink socket: " +
                               std::string{nl_geterror(err)}};
    }
  }

  Socket(const Socket &) = delete;
  Socket &operator=(const Socket &) = delete;
  Socket(Socket &&) = delete;
  Socket &operator=(Socket &&) = delete;

  ~Socket() { nl_socket_free(handle); }

  struct nl_sock *handle;
};

/**
 * Read all links from netlink socket.
 */
struct LinkCache {
  explicit LinkCache(Socket *socket) : handle{nullptr} {
    if (int err = rtnl_link_alloc_cache(socket->handle, AF_UNSPEC, &handle);
        err < 0) {
      throw std::runtime_error{"Unable to allocate link cache: " +
                               std::string{nl_geterror(err)}};
    }
  }

  LinkCache(const LinkCache &) = delete;
  LinkCache &operator=(const LinkCache &) = delete;
  LinkCache(LinkCache &&) = delete;
  LinkCache &operator=(LinkCache &&) = delete;

  ~LinkCache() { nl_cache_free(handle); }

  struct nl_cache *handle;
};

/**
 * Link (such as "eth0" or "wlan0").
 */
struct Link {
  Link(LinkCache *link_cache, const std::string &iface)
      : handle{rtnl_link_get_by_name(link_cache->handle, iface.c_str())} {

    if (handle == nullptr) {
      throw std::runtime_error{"Link does not exist:" + iface};
    }
  }

  Link(const Link &) = delete;
  Link &operator=(const Link &) = delete;
  Link(Link &&) = delete;
  Link &operator=(Link &&) = delete;

  ~Link() { rtnl_link_put(handle); }

  struct rtnl_link *handle;
};

/**
 * Queuing discipline.
 */
struct QDisc {
  QDisc(const std::string &iface, const std::string &kind)
      : handle{rtnl_qdisc_alloc()} {
    if (handle == nullptr) {
      throw std::runtime_error{"Failed to allocate qdisc!"};
    }

    struct rtnl_tc *tc = TC_CAST(handle);

    // Set link
    LinkCache link_cache{&socket};
    Link link{&link_cache, iface};
    rtnl_tc_set_link(tc, link.handle);

    // Set parent qdisc
    uint32_t parent = 0;

    if (int err = rtnl_tc_str2handle("root", &parent); err < 0) {
      throw std::runtime_error{"Unable to parse handle: " +
                               std::string{nl_geterror(err)}};
    }

    rtnl_tc_set_parent(tc, parent);

    // Set kind (e.g. "plug")
    if (int err = rtnl_tc_set_kind(tc, kind.c_str()); err < 0) {
      throw std::runtime_error{"Unable to set kind: " +
                               std::string{nl_geterror(err)}};
    }
  }

  QDisc(const QDisc &) = delete;
  QDisc &operator=(const QDisc &) = delete;
  QDisc(QDisc &&) = delete;
  QDisc &operator=(QDisc &&) = delete;

  ~QDisc() {
    if (int err = rtnl_qdisc_delete(socket.handle, handle); err < 0) {
      std::cerr << "Unable to delete qdisc: " << nl_geterror(err) << std::endl;
    }

    rtnl_qdisc_put(handle);
  }

  void send_msg() {
    int flags = NLM_F_CREATE;

    if (int err = rtnl_qdisc_add(socket.handle, handle, flags); err < 0) {
      throw std::runtime_error{"Unable to add qdisc: " +
                               std::string{nl_geterror(err)}};
    }
  }

  Socket socket;
  struct rtnl_qdisc *handle;
};

/**
 * Queuing discipline for plugging traffic.
 */
class Plug {
public:
  Plug(const std::string &iface, uint32_t limit, bool enabled)
      : qdisc_{iface, "plug"}, enabled_{enabled} {

    rtnl_qdisc_plug_set_limit(qdisc_.handle, limit);
    qdisc_.send_msg();

    set_enabled(enabled_);
  }

  void set_enabled(bool enabled) {
    if (enabled) {
      rtnl_qdisc_plug_buffer(qdisc_.handle);
    } else {
      rtnl_qdisc_plug_release_indefinite(qdisc_.handle);
    }

    qdisc_.send_msg();
    enabled_ = enabled;
  }

  bool is_enabled() const { return enabled_; }

private:
  QDisc qdisc_;

  bool enabled_;
};

std::atomic<bool> quit{false};

void exit_handler(int /*signal*/) { quit = true; }

int main() {
  std::string iface{"eth0"};
  constexpr uint32_t buffer_size = 32768;
  bool enabled = true;

  Plug plug{iface, buffer_size, enabled};

  /**
   * Set custom exit handler to ensure destructor runs to delete qdisc.
   */
  struct sigaction sa {};
  sa.sa_handler = exit_handler;
  sigfillset(&sa.sa_mask);
  sigaction(SIGINT, &sa, nullptr);

  while (!quit) {
    std::cout << "Plug set to " << plug.is_enabled() << std::endl;
    std::cout << "Press <Enter> to continue.";
    std::cin.get();

    plug.set_enabled(!plug.is_enabled());
  }

  return EXIT_SUCCESS;
}

在其中设置您要使用的网络接口main函数(例如eth0 or wlan0)。该程序可以用于:

# g++ -std=c++17 -Wall -Wextra -pedantic netbuf.cpp $( pkg-config --cflags --libs libnl-3.0 libnl-route-3.0 )
# ./a.out 
Plug set to 1
Press <Enter> to continue.
Plug set to 0
Press <Enter> to continue.
Plug set to 1
Press <Enter> to continue.

(Exit with Ctrl+c.)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

内核 qdiscs 模块中的网络数据包缓冲 的相关文章

  • 如何模拟 TCP/IP 错误?

    在多层应用程序上 我需要模拟各种 TCP IP 错误来测试一些重新连接代码 有谁知道我可以使用什么工具 基于 Windows 来实现此目的 谢谢 Scapy http secdev org projects scapy 允许您控制数据包的各
  • Windows Phone 7 可以实现 ping 吗?

    为了了解 WP7 中的网络功能 我将构建一个简单的 ping 应用程序 该应用程序将显示对某个主机的 ICMP ping 请求的结果 然而 不仅System Net NetworkInformation Ping班级不见了 System N
  • 在内核模块中执行shell命令

    是否可以在内核模块中执行shell命令 我知道我们可以在用户空间 C 代码中使用system子程序 我正在调试一个存在内存泄漏问题的内核模块 在无限循环中执行 insmod 和 rmmod module ko 后 8G RAM 的系统在几分
  • 在执行期间访问.eh_frame数据

    我正在尝试访问以下内容 eh frame正在运行的程序的一部分 具体来说 该程序是 Linux 内核 2 6 34 8 这 eh frame包含用于异常处理的有用数据 我想在内核代码内部使用它 该部分已经由以下人员编写gcc readelf
  • 从内核空间中的块设备读取

    我正在编写一个内核模块 需要从现有的块设备执行读取 dev 东西 有谁知道有任何其他模块可以执行这些操作 我可以用作参考吗 欢迎任何指点 Linux 2 6 30 如果你真的绝对必须那么使用filp open filp close vfs
  • 大会,你好世界问题

    我正在 Linux 上学习 asm noobuntu 10 04 我得到了以下代码 http asm sourceforge net intro hello html http asm sourceforge net intro hello
  • OpenSSL:无需 SSL_read() / SSL_write() 即可执行加密/解密

    我已经用 C 语言编写了一个基于事件的网络库 现在我想通过 OpenSSL 添加 SSL TLS 支持 而不是使用SSL read and SSL write 我宁愿让 OpenSSL 只执行传出 传入数据的加密 解密 让我自己传输 接收数
  • Linux、ARM:为什么仅当启动时存在 I2C GPIO 扩展器时才创建 gpiochip

    在 imx6sx 硬件平台 NXP 嵌入式 ARM 上使用 Linux 3 14 52 问题是设备树中指定的 PCF8575 I2C GPIO 扩展器不会实例化为 sys class gpio 结构中的设备 除非它们在内核启动期间存在 这些
  • modinfo srcversion:如何从我的源生成此版本?

    我有一个 Linux 模块的编译版本 然后我有大约 20 多个其源代码的变体 由于各种愚蠢的错误 我已经不知道哪个版本的源代码是我用来制作模块的实际版本了 我注意到modinfo
  • 是否从页面缓存中的脏页面进行文件读取?

    当字节写入文件时 内核不会立即将这些字节写入磁盘 而是将这些字节存储在页缓存中的脏页中 回写缓存 问题是 如果在脏页刷新到磁盘之前发出文件读取 则将从缓存中的脏页提供字节 还是首先将脏页刷新到磁盘 然后进行磁盘读取以提供字节 将它们存储在进
  • 如何通过代理将套接字连接到http服务器?

    最近 我使用 C 语言编写了一个程序 用于连接到本地运行的 HTTP 服务器 从而向该服务器发出请求 这对我来说效果很好 之后 我尝试使用相同的代码连接到网络上的另一台服务器 例如 www google com 但我无法连接并从网络中的代理
  • 在网络处理中使用自旋变体

    我编写了一个与网络过滤器挂钩交互的内核模块 网络过滤器挂钩在 Softirq 上下文中运行 我正在访问全局数据结构 哈希表 来自软中断上下文以及进程上下文 进程上下文访问是由于sysctl文件用于修改哈希表的内容 我正在使用 spinloc
  • 为什么内核需要虚拟寻址?

    在Linux中 每个进程都有其虚拟地址空间 例如 32位系统为4GB 其中3GB为进程保留 1GB为内核保留 这种虚拟寻址机制有助于隔离每个进程的地址空间 对于流程来说这是可以理解的 因为有很多流程 但既然我们只有 1 个内核 那么为什么我
  • 什么是 SO_SNDBUF 和 SO_RCVBUF

    你能解释一下到底是什么吗SO SNDBUF and SO RCVBUF选项 好的 出于某种原因 操作系统缓冲传出 传入数据 但我想澄清这个主题 他们的角色 通 常 是什么 它们是每个套接字的缓冲区吗 传输层的缓冲区 例如 TCP 缓冲区 和
  • 为什么这段代码会死锁?

    我在可加载模块中创建了 2 个 Linux 内核线程 并将它们绑定到在双核 Android 设备上运行的单独 CPU 内核 运行几次后 我注意到设备重新启动并重置了硬件看门狗定时器 我一直在解决这个问题 什么可能导致僵局 基本上 我需要做的
  • 在许多驱动程序文件夹中创建 build-in.o

    我正在用我的自定义驱动程序构建内核 成功构建后 我发现了许多 build in o 文件 任何人都可以详细说明这些文件是如何在这里结束的吗 我只能怀疑这些与更高级别的 makefile 有关 built in o 文件是未构建为模块的内核的
  • 如何用 C# 发送原始以太网数据包? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 有没有办法通过 C 将原始数据包以太网发送到其他主机 在 Windows 7 中 如果有区别的话 根据 Saint pl 的建议 我找
  • 编译内核进行页表遍历时出现错误

    我正在执行页表遍历 当我准备更新内核时出现错误 kernel sys c In function do sys get page info kernel sys c 2745 23 error passing argument 1 of p
  • 为什么 call_usermodehelper 大多数时候都会失败?

    从内核模块中 我尝试使用 call usermodehelper 函数来执行可执行文件 sha1 该可执行文件将文件作为参数并将文件的 SHA1 哈希和写入另一个文件 名为输出 可执行文件完美运行 int result 1 name hom
  • 在用户程序中使用 或在驱动程序模块代码中使用 ...这有关系吗?

    我正在开发一个设备驱动程序模块和关联的用户库来处理ioctl 来电 该库获取相关信息并将其放入一个结构中 该结构被传递到驱动程序模块中并在那里解压 然后进行处理 我省略了很多步骤 但这就是总体思路 一些数据通过结构体传递ioctl is u

随机推荐