为调用tc qdisc命令的C++程序配置CAP_NET_ADMIN权限
无需sudo让C++程序调用tc配置队列规则的权限方案
问题场景
我有一个C++程序,计划通过调用iproute2的tc命令设置队列规则(queuing disciplines),示例代码如下:
#include <iostream> #include <cstdlib> int main() { const char* cmd = "tc qdisc add dev veno1 root fq_codel"; int result = std::system(cmd); if (result == 0) { std::cout << "qdisc configured successfully" << std::endl; } else { std::cerr << "failed to configure qdisc" << std::endl; } return 0; }
编译运行后输出错误:
RTNETLINK answers: Operation not permitted failed to configure qdisc
问题根源在于tc qdisc add属于特权操作,需要CAP_NET_ADMIN权限。尝试过两种方式但都不符合需求:
- 执行
sudo setcap cap_net_admin+ep /sbin/tc:可以解决问题,但权限范围过广,所有用户都能随意执行tc的特权操作 - 执行
sudo setcap cap_net_admin+eip ./myprogram:无效,因为system()调用会启动shell,shell不会继承程序的capabilities,导致最终调用的tc仍无权限
补充说明:程序计划使用TAPRIO qdisc,且这只是程序的一小部分功能,希望尽量避免用libnl实现。
可行解决方案
1. 直接调用tc二进制(绕过shell)
system()会启动shell进程,而shell默认不会继承父进程的capabilities。改用fork()+execvp()直接执行tc二进制,子进程会继承父进程的CAP_NET_ADMIN权限。
修改后的代码示例:
#include <iostream> #include <unistd.h> #include <sys/wait.h> int main() { // 拆分tc命令为参数数组 char* const args[] = { (char*)"tc", (char*)"qdisc", (char*)"add", (char*)"dev", (char*)"veno1", (char*)"root", (char*)"fq_codel", nullptr // 参数数组必须以nullptr结尾 }; pid_t pid = fork(); if (pid == -1) { std::cerr << "fork failed" << std::endl; return 1; } else if (pid == 0) { // 子进程:执行tc命令 execvp("/sbin/tc", args); // 如果execvp返回,说明执行失败 std::cerr << "execvp failed" << std::endl; _exit(1); } else { // 父进程:等待子进程结束 int status; waitpid(pid, &status, 0); if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { std::cout << "qdisc configured successfully" << std::endl; } else { std::cerr << "failed to configure qdisc" << std::endl; } } return 0; }
编译后给程序添加cap权限:
sudo setcap cap_net_admin+eip ./myprogram
此时直接运行程序即可成功执行tc命令。
2. 配置sudoers限制特定程序执行指定tc命令
如果不想修改代码,可以通过sudoers配置让目标程序无需密码执行特定的tc命令。
- 编辑sudoers文件(务必用
visudo编辑,避免语法错误):
sudo visudo
- 添加以下规则(替换
/path/to/myprogram为你的程序路径,username为运行程序的用户):
username ALL=(ALL) NOPASSWD: /sbin/tc qdisc add dev veno1 root *
这条规则限制用户只能通过该程序执行指定格式的tc命令,避免权限滥用。
- 修改程序中的命令为带sudo的形式:
const char* cmd = "sudo tc qdisc add dev veno1 root fq_codel";
编译后直接运行程序即可,无需输入sudo密码。
内容的提问来源于stack exchange,提问作者Mitja Rislakki
相关产品推荐
相关产品推荐

