ClatUtils - implement tcQdisc(Add|Replace|Del)DevClsact()
Test: atest netd_unit_test
Bug: 65674744
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: Ie71edf4b5ae60d161546073a7f2c28f7af7bcc39
diff --git a/server/ClatUtils.cpp b/server/ClatUtils.cpp
index 7936c7d..f282ab0 100644
--- a/server/ClatUtils.cpp
+++ b/server/ClatUtils.cpp
@@ -19,6 +19,8 @@
#include <errno.h>
#include <linux/if.h>
#include <linux/netlink.h>
+#include <linux/pkt_sched.h>
+#include <linux/rtnetlink.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
@@ -137,5 +139,70 @@
return resp.e.error; // returns 0 on success
}
+// ADD: nlMsgType=RTM_NEWQDISC nlMsgFlags=NLM_F_EXCL|NLM_F_CREATE
+// REPLACE: nlMsgType=RTM_NEWQDISC nlMsgFlags=NLM_F_CREATE|NLM_F_REPLACE
+// DEL: nlMsgType=RTM_DELQDISC nlMsgFlags=0
+int doTcQdiscClsact(int fd, int ifIndex, __u16 nlMsgType, __u16 nlMsgFlags) {
+ // This is the name of the qdisc we are attaching.
+ // Some hoop jumping to make this compile time constant with known size,
+ // so that the structure declaration is well defined at compile time.
+#define CLSACT "clsact"
+ static const char clsact[] = CLSACT;
+ // sizeof() includes the terminating NULL
+#define ASCIIZ_LEN_CLSACT sizeof(clsact)
+
+ const struct {
+ nlmsghdr n;
+ tcmsg t;
+ struct {
+ nlattr attr;
+ char str[NLMSG_ALIGN(ASCIIZ_LEN_CLSACT)];
+ } kind;
+ } req = {
+ .n =
+ {
+ .nlmsg_len = sizeof(req),
+ .nlmsg_type = nlMsgType,
+ .nlmsg_flags = static_cast<__u16>(NETLINK_REQUEST_FLAGS | nlMsgFlags),
+ },
+ .t =
+ {
+ .tcm_family = AF_UNSPEC,
+ .tcm_ifindex = ifIndex,
+ .tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0),
+ .tcm_parent = TC_H_CLSACT,
+ },
+ .kind =
+ {
+ .attr =
+ {
+ .nla_len = NLA_HDRLEN + ASCIIZ_LEN_CLSACT,
+ .nla_type = TCA_KIND,
+ },
+ .str = CLSACT,
+ },
+ };
+#undef ASCIIZ_LEN_CLSACT
+#undef CLSACT
+
+ const int rv = send(fd, &req, sizeof(req), 0);
+ if (rv == -1) return -errno;
+ if (rv != sizeof(req)) return -EMSGSIZE;
+
+ return processNetlinkResponse(fd);
+}
+
+int tcQdiscAddDevClsact(int fd, int ifIndex) {
+ return doTcQdiscClsact(fd, ifIndex, RTM_NEWQDISC, NLM_F_EXCL | NLM_F_CREATE);
+}
+
+int tcQdiscReplaceDevClsact(int fd, int ifIndex) {
+ return doTcQdiscClsact(fd, ifIndex, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_REPLACE);
+}
+
+int tcQdiscDelDevClsact(int fd, int ifIndex) {
+ return doTcQdiscClsact(fd, ifIndex, RTM_DELQDISC, 0);
+}
+
} // namespace net
} // namespace android
diff --git a/server/ClatUtils.h b/server/ClatUtils.h
index 2788cb5..6c3681f 100644
--- a/server/ClatUtils.h
+++ b/server/ClatUtils.h
@@ -32,6 +32,10 @@
int processNetlinkResponse(int fd);
+int tcQdiscAddDevClsact(int fd, int ifIndex);
+int tcQdiscReplaceDevClsact(int fd, int ifIndex);
+int tcQdiscDelDevClsact(int fd, int ifIndex);
+
} // namespace net
} // namespace android
diff --git a/server/ClatUtilsTest.cpp b/server/ClatUtilsTest.cpp
index 7fb0d5d..8c21528 100644
--- a/server/ClatUtilsTest.cpp
+++ b/server/ClatUtilsTest.cpp
@@ -93,5 +93,24 @@
close(fd);
}
+// See Linux kernel source in include/net/flow.h
+#define LOOPBACK_IFINDEX 1
+
+TEST_F(ClatUtilsTest, AttachReplaceDetachClsactLo) {
+ // Technically does not depend on ebpf, but does depend on clsact,
+ // and we do not really care if it works on pre-4.9-Q anyway.
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ int fd = openNetlinkSocket();
+ ASSERT_LE(3, fd);
+
+ // This attaches and detaches a configuration-less and thus no-op clsact
+ // qdisc to loopback interface (and it takes fractions of a second)
+ EXPECT_EQ(0, tcQdiscAddDevClsact(fd, LOOPBACK_IFINDEX));
+ EXPECT_EQ(0, tcQdiscReplaceDevClsact(fd, LOOPBACK_IFINDEX));
+ EXPECT_EQ(0, tcQdiscDelDevClsact(fd, LOOPBACK_IFINDEX));
+ close(fd);
+}
+
} // namespace net
} // namespace android