Commit 9e1f7a31 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'tools-ynl-gen-support-sub-messages-and-rt-link'

Jakub Kicinski says:

====================
tools: ynl-gen: support sub-messages and rt-link

Sub-messages are how we express "polymorphism" in YNL. Donald added
the support to specs and Python a while back, support them in C, too.
Sub-message is a nest, but the interpretation of the attribute types
within that nest depends on a value of another attribute. For example
in rt-link the "kind" attribute contains the link type (veth, bonding,
etc.) and based on that the right enum has to be applied to interpret
link-specific attributes.

The last message is probably the most interesting to look at, as it
adds a fairly advanced sample.

This patch only contains enough support for rtnetlink, we will need
a little more complexity to support TC, where sub-messages may contain
fixed headers, and where the selector may be in a different nest than
the submessage.
====================

Link: https://patch.msgid.link/20250515231650.1325372-1-kuba@kernel.org


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 622b91e0 d5d1813b
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -594,6 +594,7 @@ definitions:
        name: reasm-overlaps
  - name: br-boolopt-multi
    type: struct
    header: linux/if_bridge.h
    members:
      -
        name: optval
@@ -826,6 +827,8 @@ definitions:
      - name: default
  -
    name: ovpn-mode
    enum-name: ovpn-mode
    name-prefix: ovpn-mode
    type: enum
    entries:
      - p2p
@@ -2195,6 +2198,7 @@ attribute-sets:
        type: u16
  -
    name: linkinfo-ovpn-attrs
    name-prefix: ifla-ovpn-
    attributes:
      -
        name: mode
+4 −0
Original line number Diff line number Diff line
@@ -33,5 +33,9 @@ CFLAGS_ovs_flow:=$(call get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h)
CFLAGS_ovs_vport:=$(call get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h)
CFLAGS_rt-addr:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
	$(call get_hdr_inc,__LINUX_IF_ADDR_H,if_addr.h)
CFLAGS_rt-link:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h) \
	$(call get_hdr_inc,_LINUX_IF_LINK_H,if_link.h)
CFLAGS_rt-neigh:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h)
CFLAGS_rt-route:=$(call get_hdr_inc,__LINUX_RTNETLINK_H,rtnetlink.h)
CFLAGS_rt-rule:=$(call get_hdr_inc,__LINUX_FIB_RULES_H,fib_rules.h)
CFLAGS_tcp_metrics:=$(call get_hdr_inc,_LINUX_TCP_METRICS_H,tcp_metrics.h)
+3 −4
Original line number Diff line number Diff line
@@ -22,10 +22,9 @@ TOOL:=../pyynl/ynl_gen_c.py
TOOL_RST:=../pyynl/ynl_gen_rst.py

SPECS_DIR:=../../../../Documentation/netlink/specs
GENS_PATHS=$(shell grep -nrI --files-without-match \
		'protocol: netlink' \
		$(SPECS_DIR))
GENS=$(patsubst $(SPECS_DIR)/%.yaml,%,${GENS_PATHS}) rt-addr rt-route
SPECS_PATHS=$(wildcard $(SPECS_DIR)/*.yaml)
GENS_UNSUP=conntrack nftables tc
GENS=$(filter-out ${GENS_UNSUP},$(patsubst $(SPECS_DIR)/%.yaml,%,${SPECS_PATHS}))
SRCS=$(patsubst %,%-user.c,${GENS})
HDRS=$(patsubst %,%-user.h,${GENS})
OBJS=$(patsubst %,%-user.o,${GENS})
+7 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ enum ynl_policy_type {
	YNL_PT_UINT,
	YNL_PT_NUL_STR,
	YNL_PT_BITFIELD32,
	YNL_PT_SUBMSG,
};

enum ynl_parse_result {
@@ -42,7 +43,10 @@ typedef int (*ynl_parse_cb_t)(const struct nlmsghdr *nlh,
			      struct ynl_parse_arg *yarg);

struct ynl_policy_attr {
	enum ynl_policy_type type;
	enum ynl_policy_type type:8;
	__u8 is_submsg:1;
	__u8 is_selector:1;
	__u16 selector_type;
	unsigned int len;
	const char *name;
	const struct ynl_policy_nest *nest;
@@ -103,6 +107,8 @@ struct nlmsghdr *
ynl_gemsg_start_dump(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version);

int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr);
int ynl_submsg_failed(struct ynl_parse_arg *yarg, const char *field_name,
		      const char *sel_name);

/* YNL specific helpers used by the auto-generated code */

+84 −9
Original line number Diff line number Diff line
@@ -45,8 +45,39 @@
#define perr(_ys, _msg)			__yerr(&(_ys)->err, errno, _msg)

/* -- Netlink boiler plate */
static bool
ynl_err_walk_is_sel(const struct ynl_policy_nest *policy,
		    const struct nlattr *attr)
{
	unsigned int type = ynl_attr_type(attr);

	return policy && type <= policy->max_attr &&
		policy->table[type].is_selector;
}

static const struct ynl_policy_nest *
ynl_err_walk_sel_policy(const struct ynl_policy_attr *policy_attr,
			const struct nlattr *selector)
{
	const struct ynl_policy_nest *policy = policy_attr->nest;
	const char *sel;
	unsigned int i;

	if (!policy_attr->is_submsg)
		return policy;

	sel = ynl_attr_get_str(selector);
	for (i = 0; i <= policy->max_attr; i++) {
		if (!strcmp(sel, policy->table[i].name))
			return policy->table[i].nest;
	}

	return NULL;
}

static int
ynl_err_walk_report_one(const struct ynl_policy_nest *policy, unsigned int type,
ynl_err_walk_report_one(const struct ynl_policy_nest *policy,
			const struct nlattr *selector, unsigned int type,
			char *str, int str_sz, int *n)
{
	if (!policy) {
@@ -67,9 +98,34 @@ ynl_err_walk_report_one(const struct ynl_policy_nest *policy, unsigned int type,
		return 1;
	}

	if (*n < str_sz)
		*n += snprintf(str, str_sz - *n,
	if (*n < str_sz) {
		int sz;

		sz = snprintf(str, str_sz - *n,
			      ".%s", policy->table[type].name);
		*n += sz;
		str += sz;
	}

	if (policy->table[type].is_submsg) {
		if (!selector) {
			if (*n < str_sz)
				*n += snprintf(str, str_sz, "(!selector)");
			return 1;
		}

		if (ynl_attr_type(selector) !=
		    policy->table[type].selector_type) {
			if (*n < str_sz)
				*n += snprintf(str, str_sz, "(!=selector)");
			return 1;
		}

		if (*n < str_sz)
			*n += snprintf(str, str_sz - *n, "(%s)",
				       ynl_attr_get_str(selector));
	}

	return 0;
}

@@ -78,6 +134,8 @@ ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off,
	     const struct ynl_policy_nest *policy, char *str, int str_sz,
	     const struct ynl_policy_nest **nest_pol)
{
	const struct ynl_policy_nest *next_pol;
	const struct nlattr *selector = NULL;
	unsigned int astart_off, aend_off;
	const struct nlattr *attr;
	unsigned int data_len;
@@ -96,6 +154,10 @@ ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off,
	ynl_attr_for_each_payload(start, data_len, attr) {
		astart_off = (char *)attr - (char *)start;
		aend_off = (char *)ynl_attr_data_end(attr) - (char *)start;

		if (ynl_err_walk_is_sel(policy, attr))
			selector = attr;

		if (aend_off <= off)
			continue;

@@ -109,16 +171,20 @@ ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off,

	type = ynl_attr_type(attr);

	if (ynl_err_walk_report_one(policy, type, str, str_sz, &n))
	if (ynl_err_walk_report_one(policy, selector, type, str, str_sz, &n))
		return n;

	next_pol = ynl_err_walk_sel_policy(&policy->table[type], selector);
	if (!next_pol)
		return n;

	if (!off) {
		if (nest_pol)
			*nest_pol = policy->table[type].nest;
			*nest_pol = next_pol;
		return n;
	}

	if (!policy->table[type].nest) {
	if (!next_pol) {
		if (n < str_sz)
			n += snprintf(str, str_sz, "!nest");
		return n;
@@ -128,7 +194,7 @@ ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off,
	start =  ynl_attr_data(attr);
	end = start + ynl_attr_data_len(attr);

	return n + ynl_err_walk(ys, start, end, off, policy->table[type].nest,
	return n + ynl_err_walk(ys, start, end, off, next_pol,
				&str[n], str_sz - n, nest_pol);
}

@@ -231,7 +297,7 @@ ynl_ext_ack_check(struct ynl_sock *ys, const struct nlmsghdr *nlh,
		}

		n2 = 0;
		ynl_err_walk_report_one(nest_pol, type, &miss_attr[n],
		ynl_err_walk_report_one(nest_pol, NULL, type, &miss_attr[n],
					sizeof(miss_attr) - n, &n2);
		n += n2;

@@ -384,6 +450,15 @@ int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr)
	return 0;
}

int ynl_submsg_failed(struct ynl_parse_arg *yarg, const char *field_name,
		      const char *sel_name)
{
	yerr(yarg->ys, YNL_ERROR_SUBMSG_KEY,
	     "Parsing error: Sub-message key not set (msg %s, key %s)",
	     field_name, sel_name);
	return YNL_PARSE_CB_ERROR;
}

/* Generic code */

static void ynl_err_reset(struct ynl_sock *ys)
Loading