[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250912195339.20635-13-yana2bsh@gmail.com>
Date: Fri, 12 Sep 2025 22:53:35 +0300
From: Yana Bashlykova <yana2bsh@...il.com>
To: "David S. Miller" <davem@...emloft.net>
Cc: Yana Bashlykova <yana2bsh@...il.com>,
Eric Dumazet <edumazet@...gle.com>,
Jakub Kicinski <kuba@...nel.org>,
Paolo Abeni <pabeni@...hat.com>,
Shuah Khan <shuah@...nel.org>,
netdev@...r.kernel.org,
linux-kselftest@...r.kernel.org,
linux-kernel@...r.kernel.org,
lvc-project@...uxtesting.org
Subject: [PATCH 6.1 12/15] selftests: net: genetlink: add Generic Netlink controller tests
Add comprehensive test coverage for nlctrl (Generic Netlink controller)
Add my_genl_ctrl_resolve function for resolving family ID
Signed-off-by: Yana Bashlykova <yana2bsh@...il.com>
---
tools/testing/selftests/net/genetlink.c | 925 ++++++++++++++++++++++++
1 file changed, 925 insertions(+)
diff --git a/tools/testing/selftests/net/genetlink.c b/tools/testing/selftests/net/genetlink.c
index f8231a302c36..0a05402caa20 100644
--- a/tools/testing/selftests/net/genetlink.c
+++ b/tools/testing/selftests/net/genetlink.c
@@ -81,6 +81,404 @@
#define LARGE_GENL_FAMILY_NAME "LARGE_GENL"
+/**
+ * Callback data structures - used to pass data between test cases and message handlers
+ */
+
+struct callback_data_ctrl {
+ int family_id;
+ char *family_name;
+ int op;
+ struct expected_policies *expected_policy;
+ int family_index;
+};
+
+static int elem;
+
+static int id_elem;
+
+struct ctrl_policy {
+ int id;
+ uint32_t field;
+ uint32_t type;
+ int value;
+};
+
+struct ctrl_policy expected_parallel_policy[] = {
+ { 1, CTRL_ATTR_OP_POLICY, CTRL_ATTR_POLICY_DO, 0 },
+ { 2, CTRL_ATTR_OP_POLICY, CTRL_ATTR_POLICY_DUMP, 0 },
+ { 3, CTRL_ATTR_OP_POLICY, CTRL_ATTR_POLICY_DO, 1 },
+ { 4, CTRL_ATTR_OP_POLICY, CTRL_ATTR_POLICY_DO, 0 },
+ { 5, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_TYPE, 11 },
+ { 6, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_TYPE, 10 },
+ { 7, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_TYPE, 12 },
+ { 8, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_TYPE, 12 },
+ { 9, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_TYPE, 15 },
+ { 9, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_BITFIELD32_MASK, 0 },
+ { 10, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_TYPE, 8 },
+ { 10, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_MIN_VALUE_S, -100 },
+ { 10, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_MAX_VALUE_S, 100 },
+ { 11, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_TYPE, 14 },
+ { 12, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_TYPE, 13 },
+ { 13, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_TYPE, 1 },
+ { 14, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_TYPE, 1 },
+ { 15, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_TYPE, 11 },
+};
+
+struct ctrl_policy expected_genl_cmd_get_value_policy[] = {
+ { 1, CTRL_ATTR_OP_POLICY, CTRL_ATTR_POLICY_DO, 0 },
+ { 2, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_TYPE, 11 },
+ { 3, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_TYPE, 4 },
+ { 3, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_MIN_VALUE_U, 0 },
+ { 3, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_MAX_VALUE_U, 100 },
+ { 4, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_TYPE, 11 },
+ { 5, CTRL_ATTR_POLICY, NL_POLICY_TYPE_ATTR_TYPE, 13 },
+};
+
+struct expected_policies {
+ struct ctrl_policy *policy;
+ int count;
+ int matched;
+};
+
+struct expected_policies parallel_policy = {
+ .policy = expected_parallel_policy,
+ .count = sizeof(expected_parallel_policy) /
+ sizeof(expected_parallel_policy[0]),
+ .matched = 0,
+};
+
+struct expected_policies genl_cmd_get_value_policy = {
+ .policy = expected_genl_cmd_get_value_policy,
+ .count = sizeof(expected_genl_cmd_get_value_policy) /
+ sizeof(expected_genl_cmd_get_value_policy[0]),
+ .matched = 0,
+};
+
+int validate_cb_ctrl(struct nl_msg *msg, void *arg)
+{
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *attrs[CTRL_ATTR_MAX + 1];
+ int ret = 0;
+ int family_id = -40;
+ char *family_name = NULL;
+
+ ret = genlmsg_parse(nlmsg_hdr(msg), 0, attrs, CTRL_ATTR_MAX, NULL);
+ if (ret < 0) {
+ printf("Failed to parse attributes: %d\n", ret);
+ return NL_STOP;
+ }
+
+ struct callback_data_ctrl *data_ctrl = (struct callback_data_ctrl *)arg;
+
+ switch (gnlh->cmd) {
+ case CTRL_CMD_NEWFAMILY:
+ if (attrs[CTRL_ATTR_FAMILY_ID]) {
+ if (data_ctrl->family_name) {
+ family_name = nla_get_string(
+ attrs[CTRL_ATTR_FAMILY_NAME]);
+ if (!strcmp(family_name,
+ data_ctrl->family_name)) {
+ family_id = nla_get_u16(
+ attrs[CTRL_ATTR_FAMILY_ID]);
+ data_ctrl->family_id = family_id;
+ }
+ }
+ }
+ if (attrs[CTRL_ATTR_FAMILY_NAME]) {
+ if (data_ctrl->family_id) {
+ if (!data_ctrl->family_name) {
+ family_name = nla_get_string(
+ attrs[CTRL_ATTR_FAMILY_NAME]);
+ data_ctrl->family_name = family_name;
+ }
+ }
+ }
+ data_ctrl->family_index++;
+ return NL_OK;
+
+ case CTRL_CMD_GETPOLICY:
+ struct ctrl_policy *exp =
+ &data_ctrl->expected_policy->policy[elem];
+ if (attrs[CTRL_ATTR_FAMILY_ID]) {
+ family_id = nla_get_u16(attrs[CTRL_ATTR_FAMILY_ID]);
+ data_ctrl->family_id = family_id;
+ }
+
+ if (attrs[CTRL_ATTR_OP_POLICY]) {
+ struct nlattr *nla;
+ int rem;
+
+ nla_for_each_nested(nla, attrs[CTRL_ATTR_OP_POLICY],
+ rem) {
+ struct nlattr *tb[CTRL_ATTR_POLICY_MAX + 1] = {
+ NULL
+ };
+
+ int err = nla_parse_nested(
+ tb, CTRL_ATTR_POLICY_MAX, nla, NULL);
+ if (err < 0) {
+ printf("Failed to parse nested policy attributes: %d\n",
+ err);
+ continue;
+ }
+
+ if (tb[CTRL_ATTR_POLICY_DO]) {
+ uint32_t do_id = nla_get_u32(
+ tb[CTRL_ATTR_POLICY_DO]);
+ if (exp->field == CTRL_ATTR_OP_POLICY &&
+ exp->type == CTRL_ATTR_POLICY_DO &&
+ exp->value == do_id) {
+ data_ctrl->expected_policy
+ ->matched++;
+ elem++;
+ if (elem != id_elem) {
+ exp = &data_ctrl
+ ->expected_policy
+ ->policy[elem];
+ }
+ }
+ }
+
+ if (tb[CTRL_ATTR_POLICY_DUMP]) {
+ uint32_t dump_id = nla_get_u32(
+ tb[CTRL_ATTR_POLICY_DUMP]);
+ if (exp->field == CTRL_ATTR_OP_POLICY &&
+ exp->type ==
+ CTRL_ATTR_POLICY_DUMP &&
+ exp->value == dump_id) {
+ data_ctrl->expected_policy
+ ->matched++;
+ elem++;
+ if (elem != id_elem) {
+ exp = &data_ctrl
+ ->expected_policy
+ ->policy[elem];
+ }
+ }
+ }
+ }
+ id_elem++;
+ }
+
+ if (attrs[CTRL_ATTR_POLICY]) {
+ struct nlattr *policy_attr;
+ int rem;
+
+ nla_for_each_nested(policy_attr,
+ attrs[CTRL_ATTR_POLICY], rem) {
+ struct nlattr *tb[NL_POLICY_TYPE_ATTR_MAX +
+ 1] = { NULL };
+
+ int err = nla_parse_nested(
+ tb, NL_POLICY_TYPE_ATTR_MAX,
+ nla_data(policy_attr), NULL);
+ if (err < 0) {
+ printf("Failed to parse nested policy attributes: %d\n",
+ err);
+ continue;
+ }
+
+ if (tb[NL_POLICY_TYPE_ATTR_TYPE]) {
+ uint32_t value1 = nla_get_u32(
+ tb[NL_POLICY_TYPE_ATTR_TYPE]);
+ if (exp->field == CTRL_ATTR_POLICY &&
+ exp->type ==
+ NL_POLICY_TYPE_ATTR_TYPE &&
+ exp->value == value1) {
+ data_ctrl->expected_policy
+ ->matched++;
+ elem++;
+ if (elem != id_elem) {
+ exp = &data_ctrl
+ ->expected_policy
+ ->policy[elem];
+ }
+ }
+ }
+
+ if (tb[NL_POLICY_TYPE_ATTR_MIN_VALUE_S]) {
+ int64_t value2 = nla_get_s64(
+ tb[NL_POLICY_TYPE_ATTR_MIN_VALUE_S]);
+ if (exp->field == CTRL_ATTR_POLICY &&
+ exp->type ==
+ NL_POLICY_TYPE_ATTR_MIN_VALUE_S &&
+ exp->value == value2) {
+ data_ctrl->expected_policy
+ ->matched++;
+ elem++;
+ if (elem != id_elem) {
+ exp = &data_ctrl
+ ->expected_policy
+ ->policy[elem];
+ }
+ }
+ }
+
+ if (tb[NL_POLICY_TYPE_ATTR_MAX_VALUE_S]) {
+ int64_t value3 = nla_get_s64(
+ tb[NL_POLICY_TYPE_ATTR_MAX_VALUE_S]);
+ if (exp->field == CTRL_ATTR_POLICY &&
+ exp->type ==
+ NL_POLICY_TYPE_ATTR_MAX_VALUE_S &&
+ exp->value == value3) {
+ data_ctrl->expected_policy
+ ->matched++;
+ elem++;
+ if (elem != id_elem) {
+ exp = &data_ctrl
+ ->expected_policy
+ ->policy[elem];
+ }
+ }
+ }
+
+ if (tb[NL_POLICY_TYPE_ATTR_MIN_VALUE_U]) {
+ uint64_t value4 = nla_get_u64(
+ tb[NL_POLICY_TYPE_ATTR_MIN_VALUE_U]);
+ if (exp->field == CTRL_ATTR_POLICY &&
+ exp->type ==
+ NL_POLICY_TYPE_ATTR_MIN_VALUE_U &&
+ exp->value == value4) {
+ data_ctrl->expected_policy
+ ->matched++;
+ elem++;
+ if (elem != id_elem) {
+ exp = &data_ctrl
+ ->expected_policy
+ ->policy[elem];
+ }
+ }
+ }
+
+ if (tb[NL_POLICY_TYPE_ATTR_MAX_VALUE_U]) {
+ uint64_t value5 = nla_get_u64(
+ tb[NL_POLICY_TYPE_ATTR_MAX_VALUE_U]);
+ if (exp->field == CTRL_ATTR_POLICY &&
+ exp->type ==
+ NL_POLICY_TYPE_ATTR_MAX_VALUE_U &&
+ exp->value == value5) {
+ data_ctrl->expected_policy
+ ->matched++;
+ elem++;
+ }
+ }
+ if (tb[NL_POLICY_TYPE_ATTR_MIN_LENGTH]) {
+ uint32_t value6 = nla_get_u32(
+ tb[NL_POLICY_TYPE_ATTR_MIN_LENGTH]);
+ if (exp->field == CTRL_ATTR_POLICY &&
+ exp->type ==
+ NL_POLICY_TYPE_ATTR_MIN_LENGTH &&
+ exp->value == value6) {
+ data_ctrl->expected_policy
+ ->matched++;
+ elem++;
+ if (elem != id_elem) {
+ exp = &data_ctrl
+ ->expected_policy
+ ->policy[elem];
+ }
+ }
+ }
+
+ if (tb[NL_POLICY_TYPE_ATTR_MAX_LENGTH]) {
+ uint32_t value7 = nla_get_u32(
+ tb[NL_POLICY_TYPE_ATTR_MAX_LENGTH]);
+ if (exp->field == CTRL_ATTR_POLICY &&
+ exp->type ==
+ NL_POLICY_TYPE_ATTR_MAX_LENGTH &&
+ exp->value == value7) {
+ data_ctrl->expected_policy
+ ->matched++;
+ elem++;
+ if (elem != id_elem) {
+ exp = &data_ctrl
+ ->expected_policy
+ ->policy[elem];
+ }
+ }
+ }
+ if (tb[NL_POLICY_TYPE_ATTR_POLICY_IDX]) {
+ uint32_t value8 = nla_get_u32(
+ tb[NL_POLICY_TYPE_ATTR_POLICY_IDX]);
+ if (exp->field == CTRL_ATTR_POLICY &&
+ exp->type ==
+ NL_POLICY_TYPE_ATTR_POLICY_IDX &&
+ exp->value == value8) {
+ data_ctrl->expected_policy
+ ->matched++;
+ elem++;
+ if (elem != id_elem) {
+ exp = &data_ctrl
+ ->expected_policy
+ ->policy[elem];
+ }
+ }
+ }
+
+ if (tb[NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE]) {
+ uint32_t value9 = nla_get_u32(
+ tb[NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE]);
+ if (exp->field == CTRL_ATTR_POLICY &&
+ exp->type ==
+ NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE &&
+ exp->value == value9) {
+ data_ctrl->expected_policy
+ ->matched++;
+ elem++;
+ if (elem != id_elem) {
+ exp = &data_ctrl
+ ->expected_policy
+ ->policy[elem];
+ }
+ }
+ }
+ if (tb[NL_POLICY_TYPE_ATTR_BITFIELD32_MASK]) {
+ uint32_t value10 = nla_get_u32(
+ tb[NL_POLICY_TYPE_ATTR_BITFIELD32_MASK]);
+ if (exp->field == CTRL_ATTR_POLICY &&
+ exp->type ==
+ NL_POLICY_TYPE_ATTR_BITFIELD32_MASK &&
+ exp->value == value10) {
+ data_ctrl->expected_policy
+ ->matched++;
+ elem++;
+ if (elem != id_elem) {
+ exp = &data_ctrl
+ ->expected_policy
+ ->policy[elem];
+ }
+ }
+ }
+
+ if (tb[NL_POLICY_TYPE_ATTR_PAD]) {
+ uint64_t value11 = nla_get_u64(
+ tb[NL_POLICY_TYPE_ATTR_PAD]);
+ if (exp->field == CTRL_ATTR_POLICY &&
+ exp->type ==
+ NL_POLICY_TYPE_ATTR_PAD &&
+ exp->value == value11) {
+ data_ctrl->expected_policy
+ ->matched++;
+ elem++;
+ if (elem != id_elem) {
+ exp = &data_ctrl
+ ->expected_policy
+ ->policy[elem];
+ }
+ }
+ }
+ }
+ id_elem++;
+ }
+ return NL_OK;
+ default:
+ printf("Unknown command: %u\n", gnlh->cmd);
+ break;
+ }
+ return NL_OK;
+}
+
struct nl_sock *socket_alloc_and_conn(void)
{
struct nl_sock *socket;
@@ -100,6 +498,100 @@ struct nl_sock *socket_alloc_and_conn(void)
return socket;
}
+int my_genl_ctrl_resolve(char *family_name)
+{
+ struct nl_sock *ctrl_sock;
+ int genl_ctrl_family_id;
+ struct nl_msg *msg;
+ struct genlmsghdr *user_hdr;
+ struct nl_cb *cb_ctrl;
+ int err = -100;
+ struct callback_data_ctrl cb_ctrl_data;
+
+ cb_ctrl_data.family_name = family_name;
+
+ ctrl_sock = socket_alloc_and_conn();
+ if (!ctrl_sock) {
+ fprintf(stderr, "socket for genl_ctrl is NULL\n");
+ return -ENOMEM;
+ }
+
+ genl_ctrl_family_id = genl_ctrl_resolve(ctrl_sock, GENL_CTRL);
+ if (genl_ctrl_family_id < 0) {
+ fprintf(stderr,
+ "Failed to resolve family id for genl_ctrl: %d\n",
+ genl_ctrl_family_id);
+ err = genl_ctrl_family_id;
+ return err;
+ }
+
+ msg = nlmsg_alloc();
+ if (!msg) {
+ fprintf(stderr, "Failed to allocate message\n");
+ nl_socket_free(ctrl_sock);
+ return -ENOMEM;
+ }
+
+ user_hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ,
+ genl_ctrl_family_id, 0,
+ NLM_F_REQUEST | NLM_F_DUMP, CTRL_CMD_GETFAMILY,
+ 0);
+ if (!user_hdr) {
+ fprintf(stderr, "Failed to genlmsg_put\n");
+ nlmsg_free(msg);
+ nl_socket_free(ctrl_sock);
+ return -ENOMEM;
+ }
+
+ if (nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family_name) < 0) {
+ fprintf(stderr,
+ "Failed to add CTRL_ATTR_FAMILY_NAME attribute: %s\n",
+ strerror(errno));
+ nlmsg_free(msg);
+ nl_socket_free(ctrl_sock);
+ return -EMSGSIZE;
+ }
+
+ cb_ctrl = nl_cb_alloc(NL_CB_DEFAULT);
+ if (!cb_ctrl) {
+ fprintf(stderr, "Failed to allocate callback\n");
+ nlmsg_free(msg);
+ nl_socket_free(ctrl_sock);
+ return -ENOMEM;
+ }
+
+ err = nl_cb_set(cb_ctrl, NL_CB_VALID, NL_CB_CUSTOM, validate_cb_ctrl,
+ &cb_ctrl_data);
+ if (err < 0) {
+ printf("Error setting callback\n");
+ goto error;
+ }
+
+ err = nl_send_auto(ctrl_sock, msg);
+ if (err < 0) {
+ fprintf(stderr, "Failed to send message: %s\n",
+ nl_geterror(err));
+ goto error;
+ }
+
+ err = nl_recvmsgs(ctrl_sock, cb_ctrl);
+ if (err < 0) {
+ fprintf(stderr, "Failed to receive message: %s\n",
+ nl_geterror(err));
+ goto error;
+ }
+
+ nlmsg_free(msg);
+ nl_cb_put(cb_ctrl);
+ nl_socket_free(ctrl_sock);
+ return cb_ctrl_data.family_id;
+error:
+ nlmsg_free(msg);
+ nl_cb_put(cb_ctrl);
+ nl_socket_free(ctrl_sock);
+ return err;
+}
+
/*
* Test cases
*/
@@ -217,6 +709,439 @@ TEST(open_netlink_file)
fclose(file);
}
+/**
+ * TEST(genl_ctrl_one_family) - Tests resolution of single Generic Netlink family
+ *
+ * Validates that:
+ * 1. Controller correctly resolves family ID for given family name
+ * 2. Family ID obtained through direct query matches cached resolution
+ * 3. Callback correctly processes controller response
+ *
+ * Test flow:
+ * 1. Creates control socket
+ * 2. Sends GETFAMILY request for target family
+ * 3. Validates response through callback
+ * 4. Compares with direct resolution result
+ */
+
+TEST(genl_ctrl_one_family)
+{
+ struct nl_sock *ctrl_sock;
+ int genl_ctrl_family_id;
+ int family_id;
+ struct nl_msg *msg;
+ struct genlmsghdr *user_hdr;
+ struct nl_cb *cb_ctrl;
+ int err = 0;
+ struct callback_data_ctrl cb_ctrl_data;
+
+ cb_ctrl_data.family_id = -30;
+ cb_ctrl_data.family_name = NULL;
+ cb_ctrl_data.op = -100;
+
+ printf("Running Test: getting family via genl_ctrl...\n");
+
+ ctrl_sock = socket_alloc_and_conn();
+ EXPECT_NE(NULL, ctrl_sock);
+ if (!ctrl_sock) {
+ fprintf(stderr, "socket for genl_ctrl is NULL\n");
+ return;
+ }
+
+ genl_ctrl_family_id = genl_ctrl_resolve(ctrl_sock, GENL_CTRL);
+ EXPECT_GT(genl_ctrl_family_id, 0);
+ if (genl_ctrl_family_id < 0) {
+ fprintf(stderr,
+ "Failed to resolve family id for genl_ctrl: %d\n",
+ genl_ctrl_family_id);
+ err = genl_ctrl_family_id;
+ return;
+ }
+
+ msg = nlmsg_alloc();
+ EXPECT_NE(NULL, msg);
+ if (!msg) {
+ fprintf(stderr, "Failed to allocate message\n");
+ nl_socket_free(ctrl_sock);
+ return;
+ }
+
+ user_hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ,
+ genl_ctrl_family_id, 0, NLM_F_REQUEST,
+ CTRL_CMD_GETFAMILY, 0);
+ EXPECT_NE(NULL, user_hdr);
+ if (!user_hdr) {
+ fprintf(stderr, "Failed to genlmsg_put\n");
+ nlmsg_free(msg);
+ nl_socket_free(ctrl_sock);
+ return;
+ }
+
+ if (nla_put_string(msg, CTRL_ATTR_FAMILY_NAME,
+ PARALLEL_GENL_FAMILY_NAME) < 0) {
+ fprintf(stderr,
+ "Failed to add CTRL_ATTR_FAMILY_NAME attribute: %s\n",
+ strerror(errno));
+ nlmsg_free(msg);
+ nl_socket_free(ctrl_sock);
+ ASSERT_EQ(0, 1);
+ return;
+ }
+ cb_ctrl_data.family_name = PARALLEL_GENL_FAMILY_NAME;
+
+ cb_ctrl = nl_cb_alloc(NL_CB_DEFAULT);
+ EXPECT_NE(NULL, cb_ctrl);
+ if (!cb_ctrl) {
+ fprintf(stderr, "Failed to allocate callback\n");
+ nlmsg_free(msg);
+ nl_socket_free(ctrl_sock);
+ return;
+ }
+
+ err = nl_cb_set(cb_ctrl, NL_CB_VALID, NL_CB_CUSTOM, validate_cb_ctrl,
+ &cb_ctrl_data);
+ EXPECT_EQ(err, 0);
+ if (err < 0) {
+ printf("Error setting callback\n");
+ goto error;
+ }
+
+ err = nl_send_auto(ctrl_sock, msg);
+ EXPECT_GE(err, 0);
+ if (err < 0) {
+ fprintf(stderr, "Failed to send message: %s\n",
+ nl_geterror(err));
+ goto error;
+ }
+
+ err = nl_recvmsgs(ctrl_sock, cb_ctrl);
+ EXPECT_EQ(err, 0);
+ if (err < 0) {
+ fprintf(stderr, "Failed to receive message: %s\n",
+ nl_geterror(err));
+ goto error;
+ }
+ my_genl_ctrl_resolve(PARALLEL_GENL_FAMILY_NAME);
+ family_id = genl_ctrl_resolve(socket_alloc_and_conn(),
+ PARALLEL_GENL_FAMILY_NAME);
+ EXPECT_GT(cb_ctrl_data.family_id, 0);
+ EXPECT_GT(family_id, 0);
+ EXPECT_EQ(cb_ctrl_data.family_id, family_id);
+
+error:
+ nlmsg_free(msg);
+ nl_cb_put(cb_ctrl);
+ nl_socket_free(ctrl_sock);
+}
+
+/**
+ * TEST(genl_ctrl_family) - Tests dumping all registered Generic Netlink families
+ *
+ * Verifies that:
+ * 1. Controller correctly responds to family dump request
+ * 2. No errors occur during dump operation
+ *
+ * Test flow:
+ * 1. Creates control socket and resolves genl_ctrl family
+ * 2. Sends GETFAMILY dump request with NLM_F_DUMP flag
+ * 3. Checks for operation success
+ */
+
+TEST(genl_ctrl_family)
+{
+ struct nl_sock *ctrl_sock;
+ int genl_ctrl_family_id;
+ struct nl_msg *msg;
+ struct genlmsghdr *user_hdr;
+ struct nl_cb *cb_ctrl;
+ int err = 0;
+ struct callback_data_ctrl cb_ctrl_data;
+
+ cb_ctrl_data.family_id = -30;
+ cb_ctrl_data.family_name = NULL;
+ cb_ctrl_data.op = -100;
+ cb_ctrl_data.family_index = 0;
+
+ printf("Running Test: getting families via genl_ctrl...\n");
+
+ ctrl_sock = socket_alloc_and_conn();
+ EXPECT_NE(NULL, ctrl_sock);
+ if (!ctrl_sock) {
+ fprintf(stderr, "socket for genl_ctrl is NULL\n");
+ return;
+ }
+
+ genl_ctrl_family_id = genl_ctrl_resolve(ctrl_sock, GENL_CTRL);
+ EXPECT_GT(genl_ctrl_family_id, 0);
+ if (genl_ctrl_family_id < 0) {
+ fprintf(stderr,
+ "Failed to resolve family id for genl_ctrl: %d\n",
+ genl_ctrl_family_id);
+ err = genl_ctrl_family_id;
+ return;
+ }
+
+ msg = nlmsg_alloc();
+ EXPECT_NE(NULL, msg);
+ if (!msg) {
+ fprintf(stderr, "Failed to allocate message\n");
+ nl_socket_free(ctrl_sock);
+ return;
+ }
+
+ user_hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ,
+ genl_ctrl_family_id, 0, NLM_F_DUMP,
+ CTRL_CMD_GETFAMILY, 0);
+ EXPECT_NE(NULL, user_hdr);
+ if (!user_hdr) {
+ fprintf(stderr, "Failed to genlmsg_put\n");
+ nlmsg_free(msg);
+ nl_socket_free(ctrl_sock);
+ return;
+ }
+
+ cb_ctrl = nl_cb_alloc(NL_CB_DEFAULT);
+ EXPECT_NE(NULL, cb_ctrl);
+ if (!cb_ctrl) {
+ fprintf(stderr, "Failed to allocate callback\n");
+ nlmsg_free(msg);
+ nl_socket_free(ctrl_sock);
+ return;
+ }
+
+ err = nl_cb_set(cb_ctrl, NL_CB_VALID, NL_CB_CUSTOM, validate_cb_ctrl,
+ &cb_ctrl_data);
+ EXPECT_EQ(err, 0);
+ if (err < 0) {
+ printf("Error setting callback\n");
+ goto error;
+ }
+
+ err = nl_send_auto(ctrl_sock, msg);
+ EXPECT_GE(err, 0);
+ if (err < 0) {
+ fprintf(stderr, "Failed to send message: %s\n",
+ nl_geterror(err));
+ goto error;
+ }
+
+ err = nl_recvmsgs(ctrl_sock, cb_ctrl);
+ EXPECT_EQ(err, 0);
+ if (err < 0) {
+ fprintf(stderr, "Failed to receive message: %s\n",
+ nl_geterror(err));
+ goto error;
+ }
+ EXPECT_GE(cb_ctrl_data.family_index, 4);
+
+error:
+ nlmsg_free(msg);
+ nl_cb_put(cb_ctrl);
+ nl_socket_free(ctrl_sock);
+}
+
+/**
+ * TEST(genl_ctrl_policy) - Validates Generic Netlink policy retrieval mechanism
+ *
+ * Tests that:
+ * 1. Policy information can be retrieved by family ID and name
+ * 2. Operation-specific policies can be retrieved
+ * 3. Retrieved policies match expected structures
+ *
+ * Test sequence:
+ * 1. Retrieves general policy for PARALLEL_GENL family
+ * 2. Retrieves operation-specific policy for MY_GENL_CMD_GET_VALUE
+ * 3. Validates policy contents through callback
+ */
+
+TEST(genl_ctrl_policy)
+{
+ struct nl_sock *ctrl_sock;
+ int genl_ctrl_family_id;
+ struct nl_msg *msg;
+ struct genlmsghdr *user_hdr;
+ struct nl_cb *cb_ctrl;
+ int err = 0;
+ struct callback_data_ctrl cb_ctrl_data;
+
+ cb_ctrl_data.family_id = -30;
+ cb_ctrl_data.family_name = NULL;
+ cb_ctrl_data.op = -100;
+ cb_ctrl_data.expected_policy = ¶llel_policy;
+ cb_ctrl_data.expected_policy->matched = 0;
+
+ printf("Running Test: getting policy via genl_ctrl...\n");
+
+ ctrl_sock = socket_alloc_and_conn();
+ EXPECT_NE(NULL, ctrl_sock);
+ if (!ctrl_sock) {
+ fprintf(stderr,
+ "sockets for genl_ctrl and parallel_genl are NULL\n");
+ return;
+ }
+
+ genl_ctrl_family_id = genl_ctrl_resolve(ctrl_sock, GENL_CTRL);
+ EXPECT_GT(genl_ctrl_family_id, 0);
+ if (genl_ctrl_family_id < 0) {
+ fprintf(stderr,
+ "Failed to resolve family id for genl_ctrl: %d\n",
+ genl_ctrl_family_id);
+ nl_socket_free(ctrl_sock);
+ return;
+ }
+
+ printf("Start first message with family id and family name\n");
+ msg = nlmsg_alloc();
+ EXPECT_NE(NULL, msg);
+ if (!msg) {
+ fprintf(stderr, "Failed to allocate message\n");
+ nl_socket_free(ctrl_sock);
+ return;
+ }
+
+ user_hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ,
+ genl_ctrl_family_id, 0, NLM_F_DUMP,
+ CTRL_CMD_GETPOLICY, 0);
+ EXPECT_NE(NULL, user_hdr);
+ if (!user_hdr) {
+ fprintf(stderr, "Failed to genlmsg_put\n");
+ nlmsg_free(msg);
+ nl_socket_free(ctrl_sock);
+ return;
+ }
+
+ if (nla_put_u16(msg, CTRL_ATTR_FAMILY_ID,
+ genl_ctrl_resolve(ctrl_sock,
+ PARALLEL_GENL_FAMILY_NAME)) < 0) {
+ fprintf(stderr,
+ "Failed to add CTRL_ATTR_FAMILY_ID attribute: %s\n",
+ strerror(errno));
+ nlmsg_free(msg);
+ nl_socket_free(ctrl_sock);
+ ASSERT_EQ(0, 1);
+ return;
+ }
+ if (nla_put_string(msg, CTRL_ATTR_FAMILY_NAME,
+ PARALLEL_GENL_FAMILY_NAME) < 0) {
+ fprintf(stderr,
+ "Failed to add CTRL_ATTR_FAMILY_NAME attribute: %s\n",
+ strerror(errno));
+ nlmsg_free(msg);
+ nl_socket_free(ctrl_sock);
+ ASSERT_EQ(0, 1);
+ return;
+ }
+
+ cb_ctrl = nl_cb_alloc(NL_CB_DEFAULT);
+ EXPECT_NE(NULL, cb_ctrl);
+ if (!cb_ctrl) {
+ fprintf(stderr, "Failed to allocate callback\n");
+ nlmsg_free(msg);
+ nl_socket_free(ctrl_sock);
+ return;
+ }
+
+ err = nl_cb_set(cb_ctrl, NL_CB_VALID, NL_CB_CUSTOM, validate_cb_ctrl,
+ &cb_ctrl_data);
+ EXPECT_EQ(err, 0);
+ if (err < 0) {
+ fprintf(stderr, "Error setting callback: %s\n",
+ nl_geterror(err));
+ goto error;
+ }
+
+ err = nl_send_auto(ctrl_sock, msg);
+ EXPECT_GE(err, 0);
+ if (err < 0) {
+ fprintf(stderr, "Failed to send message: %s\n",
+ nl_geterror(err));
+ goto error;
+ }
+
+ err = nl_recvmsgs(ctrl_sock, cb_ctrl);
+ EXPECT_EQ(err, 0);
+ if (err < 0) {
+ fprintf(stderr, "Failed to receive message: %s\n",
+ nl_geterror(err));
+ goto error;
+ }
+ EXPECT_EQ(cb_ctrl_data.expected_policy->matched,
+ cb_ctrl_data.expected_policy->count);
+
+ printf("[OK] [1/2]\n");
+
+ cb_ctrl_data.expected_policy = &genl_cmd_get_value_policy;
+ cb_ctrl_data.expected_policy->matched = 0;
+ elem = 0;
+ id_elem = 0;
+
+ nlmsg_free(msg);
+
+ printf("Start second message with family name and ctrl_attr_op\n");
+ msg = nlmsg_alloc();
+ EXPECT_NE(NULL, msg);
+ if (!msg) {
+ fprintf(stderr, "Failed to allocate message\n");
+ nl_socket_free(ctrl_sock);
+ nl_cb_put(cb_ctrl);
+ return;
+ }
+
+ user_hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ,
+ genl_ctrl_family_id, 0, NLM_F_DUMP,
+ CTRL_CMD_GETPOLICY, 0);
+ EXPECT_NE(NULL, user_hdr);
+ if (!user_hdr) {
+ fprintf(stderr, "Failed to genlmsg_put\n");
+ goto error;
+ }
+
+ if (nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, MY_GENL_FAMILY_NAME) <
+ 0) {
+ fprintf(stderr,
+ "Failed to add CTRL_ATTR_FAMILY_NAME attribute: %s\n",
+ strerror(errno));
+ EXPECT_EQ(0, 1);
+ goto error;
+ }
+
+ if (nla_put_u32(msg, CTRL_ATTR_OP, MY_GENL_CMD_GET_VALUE) < 0) {
+ fprintf(stderr, "Failed to add CTRL_ATTR_OP attribute: %s\n",
+ strerror(errno));
+ EXPECT_EQ(0, 1);
+ goto error;
+ }
+
+ err = nl_send_auto(ctrl_sock, msg);
+ EXPECT_GE(err, 0);
+ if (err < 0) {
+ fprintf(stderr, "Failed to send message: %s\n",
+ nl_geterror(err));
+ goto error;
+ }
+
+ err = nl_recvmsgs(ctrl_sock, cb_ctrl);
+ EXPECT_EQ(err, 0);
+ if (err < 0) {
+ fprintf(stderr, "Failed to receive message: %s\n",
+ nl_geterror(err));
+ goto error;
+ }
+
+ EXPECT_EQ(cb_ctrl_data.expected_policy->matched,
+ cb_ctrl_data.expected_policy->count);
+ printf("[OK] [2/2]\n");
+
+ cb_ctrl_data.expected_policy->matched = 0;
+ elem = 0;
+ id_elem = 0;
+
+error:
+ nlmsg_free(msg);
+ nl_cb_put(cb_ctrl);
+ nl_socket_free(ctrl_sock);
+}
+
/**
* TEST(capture_end) - Terminates Netlink traffic monitoring session
*
--
2.34.1
Powered by blists - more mailing lists