diff mbox series

[RFC,iproute2,3/6] mnl_utils: add function to dump command policy

Message ID 20220805234155.2878160-4-jacob.e.keller@intel.com (mailing list archive)
State RFC
Delegated to: David Ahern
Headers show
Series devlink: add policy check for all attributes | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch

Commit Message

Jacob Keller Aug. 5, 2022, 11:41 p.m. UTC
Introduce a new mnlu_get_get_op_policy function which can extract the
kernel policy data for a command. This will enable checking policy to
determine whether an attribute is accepted by a netlink command.

The policy data is reported using a new mnlu_attr_policy structure. This
allows getting a single array of policy data for a command making it easy
to check if an attribute is supported. The structure does also contain the
table of pointers to the policy data attributes which would also allow
checking both type and range.

This policy information will be used in a future change to make devlink
ensure attributes it sends are supported by the reported policy for that
command.

Only one layer of policy checking is currently supported, but an additional
mnlu_gen_get_policy_idx function could be added to support nested policy
checking in the future.

Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
 include/mnl_utils.h |  27 +++++
 lib/mnl_utils.c     | 240 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 267 insertions(+)
diff mbox series

Patch

diff --git a/include/mnl_utils.h b/include/mnl_utils.h
index 2193934849e1..450f304bb302 100644
--- a/include/mnl_utils.h
+++ b/include/mnl_utils.h
@@ -11,6 +11,31 @@  struct mnlu_gen_socket {
 	uint8_t version;
 };
 
+/**
+ * struct mnlu_attr_policy
+ *
+ * Structure representing the policy for a single attribute. Pass an array of
+ * at least nlg->maxattr + 1 to mnlu_gen_get_op_policy to extract the policy
+ * for a netlink op.
+ *
+ * @attr_type: the attribute type, extracted from the NLA data
+ *
+ * @valid: if set, the attribute is accepted by the policy. If not set, the
+ *         attribute is not accepted by the policy.
+ *
+ * @tb: Pointers to the NLA data. Only remains valid until a new netlink
+ *      command is sent, as it points directly to the netlink message buffer.
+ */
+struct mnlu_attr_policy {
+	/* Pointers to the nla attributes describing the policy for this
+	 * attribute. Note that these only remain valid until the next netlink
+	 * message is sent.
+	 */
+	const struct nlattr *tb[NL_POLICY_TYPE_ATTR_MAX + 1];
+	uint32_t attr_type;
+	uint8_t valid : 1;
+};
+
 int mnlu_gen_socket_open(struct mnlu_gen_socket *nlg, const char *family_name,
 			 uint8_t version);
 void mnlu_gen_socket_close(struct mnlu_gen_socket *nlg);
@@ -30,5 +55,7 @@  int mnlu_socket_recv_run(struct mnl_socket *nl, unsigned int seq, void *buf, siz
 			 mnl_cb_t cb, void *data);
 int mnlu_gen_socket_recv_run(struct mnlu_gen_socket *nlg, mnl_cb_t cb,
 			     void *data);
+int mnlu_gen_get_op_policy(struct mnlu_gen_socket *nlg, uint32_t op, bool dump,
+			   struct mnlu_attr_policy *policy);
 
 #endif /* __MNL_UTILS_H__ */
diff --git a/lib/mnl_utils.c b/lib/mnl_utils.c
index 79bac5cfd4de..f9918afd517d 100644
--- a/lib/mnl_utils.c
+++ b/lib/mnl_utils.c
@@ -252,3 +252,243 @@  int mnlu_gen_socket_recv_run(struct mnlu_gen_socket *nlg, mnl_cb_t cb,
 				    MNL_SOCKET_BUFFER_SIZE,
 				    cb, data);
 }
+
+struct policy_info {
+	struct mnlu_gen_socket *nlg;
+	struct mnlu_attr_policy *policy;
+	uint32_t policy_idx;
+	uint32_t op;
+	bool dump;
+};
+
+static int
+parse_policy_idx_cb(const struct nlattr *attr, void *data)
+{
+	const struct nlattr **tb = data;
+	int type = mnl_attr_get_type(attr);
+
+	if (mnl_attr_type_valid(attr, CTRL_ATTR_POLICY_DUMP_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch (type) {
+	case CTRL_ATTR_POLICY_DO:
+	case CTRL_ATTR_POLICY_DUMP:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+			return MNL_CB_ERROR;
+		break;
+	default:
+		break;
+	}
+	tb[type] = attr;
+	return MNL_CB_OK;
+}
+
+static int
+parse_policy_idx(const struct nlattr *attr, struct policy_info *info)
+{
+	struct nlattr *tb[CTRL_ATTR_POLICY_DUMP_MAX + 1] = {};
+	struct nlattr *idx_attr;
+	uint32_t op;
+
+	/* The type of this nested attribute is the op */
+	op = mnl_attr_get_type(attr);
+
+	/* Skip ops we're not interested in */
+	if (op != info->op)
+		return MNL_CB_OK;
+
+	mnl_attr_parse_nested(attr, parse_policy_idx_cb, tb);
+	if (info->dump)
+		idx_attr = tb[CTRL_ATTR_POLICY_DUMP];
+	else
+		idx_attr = tb[CTRL_ATTR_POLICY_DO];
+	if (!idx_attr)
+		return MNL_CB_ERROR;
+
+	info->policy_idx = mnl_attr_get_u32(idx_attr);
+
+	return MNL_CB_OK;
+}
+
+static int
+parse_policy_attr_data_cb(const struct nlattr *attr, void *data)
+{
+	struct mnlu_attr_policy *policy = data;
+	int type = mnl_attr_get_type(attr);
+
+	/* Unknown policy type attributes are ignored */
+	if (mnl_attr_type_valid(attr, NL_POLICY_TYPE_ATTR_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch (type) {
+	case NL_POLICY_TYPE_ATTR_TYPE:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+			return MNL_CB_ERROR;
+
+		policy->attr_type = mnl_attr_get_u32(attr);
+		policy->valid = 1;
+		break;
+	case NL_POLICY_TYPE_ATTR_MIN_VALUE_S:
+		/* libmnl doesn't yet have s64 */
+		if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_MAX_VALUE_S:
+		/* libmnl doesn't yet have s64 */
+		if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_MIN_VALUE_U:
+		if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_MAX_VALUE_U:
+		if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_MIN_LENGTH:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_MAX_LENGTH:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_POLICY_IDX:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_BITFIELD32_MASK:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_MASK:
+		if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+			return MNL_CB_ERROR;
+		break;
+	default:
+		break;
+	}
+
+	policy->tb[type] = attr;
+
+	return MNL_CB_OK;
+}
+
+static int
+parse_policy_data(const struct nlattr *attr, struct policy_info *info)
+{
+	const struct nlattr *nested_attr;
+	int policy_idx, attr_id;
+
+	/* The type of this nested attribute is the policy index */
+	policy_idx = mnl_attr_get_type(attr);
+
+	/* Skip policies we're not interested in */
+	if (policy_idx != info->policy_idx)
+		return MNL_CB_OK;
+
+	if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	nested_attr = mnl_attr_get_payload(attr);
+	if (!mnl_attr_ok(nested_attr, mnl_attr_get_payload_len(attr)))
+		return MNL_CB_ERROR;
+	if (mnl_attr_validate(nested_attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (mnl_attr_type_valid(attr, info->nlg->maxattr) < 0)
+		return MNL_CB_ERROR;
+
+	attr_id = mnl_attr_get_type(nested_attr);
+
+	return mnl_attr_parse_nested(nested_attr, parse_policy_attr_data_cb,
+				     &info->policy[attr_id]);
+}
+
+static int get_policy_attrs_cb(const struct nlattr *attr, void *data)
+{
+	int type = mnl_attr_get_type(attr);
+	struct policy_info *info = data;
+	struct nlattr *nested_attr;
+
+	if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
+		return MNL_CB_ERROR;
+
+	switch (type) {
+	case CTRL_ATTR_OP_POLICY:
+	case CTRL_ATTR_POLICY:
+		if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+			return MNL_CB_ERROR;
+
+		nested_attr = mnl_attr_get_payload(attr);
+		if (!mnl_attr_ok(nested_attr, mnl_attr_get_payload_len(attr)))
+			return MNL_CB_ERROR;
+
+		if (mnl_attr_validate(nested_attr, MNL_TYPE_NESTED) < 0)
+			return MNL_CB_ERROR;
+
+		if (type == CTRL_ATTR_OP_POLICY)
+			return parse_policy_idx(nested_attr, info);
+
+		return parse_policy_data(nested_attr, info);
+	default:
+		break;
+	}
+
+	return MNL_CB_OK;
+}
+
+static int get_policy_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+
+	return mnl_attr_parse(nlh, sizeof(*genl), get_policy_attrs_cb, data);
+}
+
+/**
+ * mnlu_get_op_policy - Get policy for a specific op.
+ * @nlg: the socket to get policy on
+ * @op: the op to get the policy for
+ * @dump: if true, get the policy NLM_F_DUMP
+ * @policy: an array of size nlg->maxattr used to return policy data
+ *
+ * Uses CTRL_CMD_GETPOLICY to extract policy information from the kernel for
+ * the specified op. The data is extracted into the provided policy buffer
+ * which is expected to be of size nlg->maxattr.
+ *
+ * This function must be called after mnlu_gen_socket_open.
+ */
+int mnlu_gen_get_op_policy(struct mnlu_gen_socket *nlg, uint32_t op, bool dump,
+			   struct mnlu_attr_policy *policy)
+{
+	struct policy_info info = {};
+	struct genlmsghdr hdr = {};
+	struct nlmsghdr *nlh;
+	int err;
+
+	info.nlg = nlg;
+	info.policy = policy;
+	info.dump = dump;
+	info.op = op;
+
+	hdr.cmd = CTRL_CMD_GETPOLICY;
+	hdr.version = 0x1;
+
+	nlh = mnlu_msg_prepare(nlg->buf, GENL_ID_CTRL,
+			       NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP,
+			       &hdr, sizeof(hdr));
+
+	mnl_attr_put_u16(nlh, CTRL_ATTR_FAMILY_ID, nlg->family);
+	mnl_attr_put_u32(nlh, CTRL_ATTR_OP, op);
+
+	err = mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
+	if (err < 0)
+		return err;
+
+	return mnlu_socket_recv_run(nlg->nl, nlh->nlmsg_seq, nlg->buf,
+				    MNL_SOCKET_BUFFER_SIZE,
+				    get_policy_cb, &info);
+}