lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<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 = &parallel_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

Powered by Openwall GNU/*/Linux Powered by OpenVZ