Add comprehensive test coverage for nlctrl (Generic Netlink controller)

Add my_genl_ctrl_resolve function for resolving family ID

Signed-off-by: Yana Bashlykova <yana2...@gmail.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


Reply via email to