我有一个 C++ 程序,打算通过调用 (iproute2) tc shell 命令来设置排队规则。
示例代码:
#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 是有特权的。我正在尝试找到一种方法来授予程序管理 qdisc 的权限,而无需以 sudo 身份运行。
tc qdisc add
需要CAP_NET_ADMIN,因此以下工作有效:
sudo setcap cap_net_admin+ep /sbin/tc
但这显然不仅限于首选的程序。但是,
sudo setcap cap_net_admin+eip ./myprogram
不起作用,导致与上面相同的输出,这意味着该功能未继承/传递给 system() -shell 中的 tc
。是否有某种方法可以配置功能或以其他方式在程序内执行命令,以便 tc
具有 CAP_NET_ADMIN 功能而无需 sudo?
旁注:预期用途是与 TAPRIO qdisc 一起使用,并构成程序操作的一小部分,因此如果可能的话,我希望避免在 libnl 中执行此操作。
感谢@3CxEZiVlQ 为我指明了正确的方向。以下是一些设置和清除可继承能力和环境能力集的工作示例,如下:
环境(自 Linux 4.3 起)
这是在非特权程序的 execve(2) 中保留的一组功能。环境能力集遵循以下不变量:如果不能同时允许且可继承,则任何能力都不能是环境能力。 环境能力集可以使用prctl(2)直接修改。如果相应的允许或可继承能力被降低,则环境能力会自动降低。
#include <iostream>
#include <sys/capability.h>
#include <sys/prctl.h>
#include <unistd.h> // For sleep
void setInheritance() {
cap_t caps = cap_get_proc();
if (caps == NULL) throw std::runtime_error("Failed: cap_get_proc()");
cap_value_t capList[] = {CAP_NET_ADMIN};
if (cap_set_flag(caps, CAP_INHERITABLE, 1, capList, CAP_SET) == -1)
throw std::runtime_error("Failed: cap_set_flag() set inheritable");
if (cap_set_proc(caps) == -1) throw std::runtime_error("Failed: cap_set_proc()");
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_ADMIN, 0, 0) == -1)
throw std::runtime_error("Failed: prctl() ambient raise");
caps = cap_get_proc();
if (caps == NULL) throw std::runtime_error("Failed: cap_get_proc()");
std:: cout << "Capabilities after setInheritance(): " << cap_to_text(caps, NULL) << '\n';
}
void clearInheritance() {
cap_t caps = cap_get_proc();
if (caps == NULL) throw std::runtime_error("Failed: cap_get_proc()");
cap_value_t capList[] = {CAP_NET_ADMIN};
if (cap_set_flag(caps, CAP_INHERITABLE, 1, capList, CAP_CLEAR) == -1)
throw std::runtime_error("Failed: cap_set_flag() clear inheritable");
if (cap_set_proc(caps) == -1) throw std::runtime_error("Failed: cap_set_proc()");
}
int main() {
setInheritance(); // Add CAP_NET_ADMIN inheritance to capabilities
if (system("tc qdisc add dev veno1 root fq_codel") == 0) {
std::cout << "qdisc configured successfully" << std::endl;
} else {
std::cerr << "failed to configure qdisc" << std::endl;
}
sleep(2);
if (system("tc qdisc del dev veno1 root") == 0) {
std::cout << "qdisc deleted successfully" << std::endl;
} else {
std::cerr << "failed to delete qdisc" << std::endl;
}
sleep(2);
clearInheritance(); // Remove CAP_NET_ADMIN from capabilities, removing ambient
// Should fail - Operation not permitted
if (system("tc qdisc add dev veno1 root fq_codel") == 0) {
std::cout << "qdisc configured successfully" << std::endl;
} else {
std::cerr << "failed to configure qdisc" << std::endl;
}
return 0;
}
运行程序:
$ g++ setqdisc.cc -lcap -o setqdisc
$ sudo setcap cap_net_admin+p ./setqdisc
$ ./setqdisc
Capabilities after setInheritance(): cap_net_admin=ip
qdisc configured successfully
qdisc deleted successfully
RTNETLINK answers: Operation not permitted
failed to configure qdisc
请注意,提供二进制 Permission 功能就足够了(
setcap
与 +p
)