Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 27 May 2023 11:14:13 GMT
From:      "Alexander V. Chernikov" <melifaro@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 5f19f790b392 - main - netlink: add snl(3) support for parsing unknown-size arrays
Message-ID:  <202305271114.34RBED8O007212@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by melifaro:

URL: https://cgit.FreeBSD.org/src/commit/?id=5f19f790b3923354871ce049b84c7807ecb0cad5

commit 5f19f790b3923354871ce049b84c7807ecb0cad5
Author:     Alexander V. Chernikov <melifaro@FreeBSD.org>
AuthorDate: 2023-05-27 11:09:01 +0000
Commit:     Alexander V. Chernikov <melifaro@FreeBSD.org>
CommitDate: 2023-05-27 11:13:14 +0000

    netlink: add snl(3) support for parsing unknown-size arrays
    
    Reviewed by:    bapt
    Differential Review: https://reviews.freebsd.org/D40282
    MFC after:      2 weeks
---
 sys/netlink/netlink_snl.h               | 122 ++++++++++++++++++++++++++------
 sys/netlink/netlink_snl_route_parsers.h |  19 +++--
 2 files changed, 116 insertions(+), 25 deletions(-)

diff --git a/sys/netlink/netlink_snl.h b/sys/netlink/netlink_snl.h
index 5844994b5e47..6d97c1afd4ee 100644
--- a/sys/netlink/netlink_snl.h
+++ b/sys/netlink/netlink_snl.h
@@ -149,33 +149,40 @@ struct snl_attr_parser {
 typedef bool snl_parse_post_f(struct snl_state *ss, void *target);
 
 struct snl_hdr_parser {
-	int			hdr_off; /* aligned header size */
-	int			fp_size;
-	int			np_size;
+	uint16_t			in_hdr_size; /* Input header size */
+	uint16_t			out_size; /* Output structure size */
+	uint16_t			fp_size; /* Number of items in field parser */
+	uint16_t			np_size; /* Number of items in attribute parser */
 	const struct snl_field_parser	*fp; /* array of header field parsers */
 	const struct snl_attr_parser	*np; /* array of attribute parsers */
 	snl_parse_post_f		*cb_post; /* post-parse callback */
 };
 
-#define	SNL_DECLARE_PARSER_EXT(_name, _t, _fp, _np, _cb)	\
-static const struct snl_hdr_parser _name = {		\
-	.hdr_off = sizeof(_t),				\
-	.fp = &((_fp)[0]),				\
-	.np = &((_np)[0]),				\
-	.fp_size = NL_ARRAY_LEN(_fp),			\
-	.np_size = NL_ARRAY_LEN(_np),			\
-	.cb_post = _cb,					\
+#define	SNL_DECLARE_PARSER_EXT(_name, _sz_h_in, _sz_out, _fp, _np, _cb)	\
+static const struct snl_hdr_parser _name = {				\
+	.in_hdr_size = _sz_h_in,					\
+	.out_size = _sz_out,						\
+	.fp = &((_fp)[0]),						\
+	.np = &((_np)[0]),						\
+	.fp_size = NL_ARRAY_LEN(_fp),					\
+	.np_size = NL_ARRAY_LEN(_np),					\
+	.cb_post = _cb,							\
 }
 
-#define	SNL_DECLARE_PARSER(_name, _t, _fp, _np)		\
-	SNL_DECLARE_PARSER_EXT(_name, _t, _fp, _np, NULL)
+#define	SNL_DECLARE_PARSER(_name, _t, _fp, _np)				\
+	SNL_DECLARE_PARSER_EXT(_name, sizeof(_t), 0, _fp, _np, NULL)
 
-#define	SNL_DECLARE_ATTR_PARSER(_name, _np)		\
-static const struct snl_hdr_parser _name = {		\
-	.np = &((_np)[0]),				\
-	.np_size = NL_ARRAY_LEN(_np),			\
+#define	SNL_DECLARE_ATTR_PARSER_EXT(_name, _sz_out, _np, _cb)		\
+static const struct snl_hdr_parser _name = {				\
+	.out_size = _sz_out,						\
+	.np = &((_np)[0]),						\
+	.np_size = NL_ARRAY_LEN(_np),					\
+	.cb_post = _cb,							\
 }
 
+#define	SNL_DECLARE_ATTR_PARSER(_name, _np)				\
+	SNL_DECLARE_ATTR_PARSER_EXT(_name, 0, _np, NULL)
+
 
 static inline void *
 snl_allocz(struct snl_state *ss, int len)
@@ -471,11 +478,13 @@ static inline bool
 snl_parse_header(struct snl_state *ss, void *hdr, int len,
     const struct snl_hdr_parser *parser, void *target)
 {
+	struct nlattr *nla_head;
+
 	/* Extract fields first (if any) */
-	snl_parse_fields(ss, hdr, parser->hdr_off, parser->fp, parser->fp_size, target);
+	snl_parse_fields(ss, hdr, parser->in_hdr_size, parser->fp, parser->fp_size, target);
 
-	struct nlattr *nla_head = (struct nlattr *)(void *)((char *)hdr + parser->hdr_off);
-	bool result = snl_parse_attrs_raw(ss, nla_head, len - parser->hdr_off,
+	nla_head = (struct nlattr *)(void *)((char *)hdr + parser->in_hdr_size);
+	bool result = snl_parse_attrs_raw(ss, nla_head, len - parser->in_hdr_size,
 	    parser->np, parser->np_size, target);
 
 	if (result && parser->cb_post != NULL)
@@ -642,6 +651,78 @@ snl_attr_get_nested(struct snl_state *ss, struct nlattr *nla, const void *arg, v
 	return (snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), p, target));
 }
 
+struct snl_parray {
+	uint32_t count;
+	void **items;
+};
+
+static inline bool
+snl_attr_get_parray_sz(struct snl_state *ss, struct nlattr *container_nla,
+    uint32_t start_size, const void *arg, void *target)
+{
+	const struct snl_hdr_parser *p = (const struct snl_hdr_parser *)arg;
+	struct snl_parray *array = target;
+	struct nlattr *nla;
+	uint32_t count = 0, size = start_size;
+
+	if (p->out_size == 0)
+		return (false);
+
+	array->items = snl_allocz(ss, size * sizeof(void *));
+	if (array->items == NULL)
+		return (false);
+
+	/*
+	 * If the provided parser is an attribute parser, assume that each
+	 *  nla in the container nla is the container nla itself and parse
+	 *  the contents of this nla.
+	 * Otherwise, run the parser on raw data, assuming the header of this
+	 * data has u16 field with total size in the beginning.
+	 */
+	uint32_t data_off = 0;
+
+	if (p->in_hdr_size == 0)
+		data_off = sizeof(struct nlattr);
+
+	NLA_FOREACH(nla, NLA_DATA(container_nla), NLA_DATA_LEN(container_nla)) {
+		void *item = snl_allocz(ss, p->out_size);
+
+		if (item == NULL)
+			return (false);
+
+		void *data = (char *)(void *)nla + data_off;
+		int data_len = nla->nla_len - data_off;
+
+		if (!(snl_parse_header(ss, data, data_len, p, item)))
+			return (false);
+
+		if (count == size) {
+			uint32_t new_size = size * 2;
+			void **new_array = snl_allocz(ss, new_size *sizeof(void *));
+
+			memcpy(new_array, array->items, size * sizeof(void *));
+			array->items = new_array;
+			size = new_size;
+		}
+		array->items[count++] = item;
+	}
+	array->count = count;
+
+	return (true);
+}
+
+/*
+ * Parses and stores the unknown-size array.
+ * Assumes each array item is a container and the NLAs in the container are parsable
+ *  by the parser provided in @arg.
+ * Assumes @target is struct snl_parray
+ */
+static inline bool
+snl_attr_get_parray(struct snl_state *ss, struct nlattr *nla, const void *arg, void *target)
+{
+	return (snl_attr_get_parray_sz(ss, nla, 8, arg, target));
+}
+
 static inline bool
 snl_attr_get_nla(struct snl_state *ss __unused, struct nlattr *nla,
     const void *arg __unused, void *target)
@@ -878,6 +959,7 @@ snl_realloc_msg_buffer(struct snl_writer *nw, size_t sz)
 	memcpy(new_base, nw->base, nw->offset);
 	if (nw->hdr != NULL) {
 		int hdr_off = (char *)(nw->hdr) - nw->base;
+
 		nw->hdr = (struct nlmsghdr *)(void *)((char *)new_base + hdr_off);
 	}
 	nw->base = new_base;
diff --git a/sys/netlink/netlink_snl_route_parsers.h b/sys/netlink/netlink_snl_route_parsers.h
index c76e1cd6440a..0ab338cbe6e8 100644
--- a/sys/netlink/netlink_snl_route_parsers.h
+++ b/sys/netlink/netlink_snl_route_parsers.h
@@ -85,7 +85,9 @@ _cb_p_mp_nh(struct snl_state *ss __unused, void *_target)
 }
 #undef _IN
 #undef _OUT
-SNL_DECLARE_PARSER_EXT(_mpath_nh_parser, struct rtnexthop, _fp_p_mp_nh, _nla_p_mp_nh, _cb_p_mp_nh);
+SNL_DECLARE_PARSER_EXT(_mpath_nh_parser, sizeof(struct rtnexthop),
+		sizeof(struct rta_mpath_nh), _fp_p_mp_nh, _nla_p_mp_nh,
+		_cb_p_mp_nh);
 
 struct rta_mpath {
 	int num_nhops;
@@ -185,7 +187,9 @@ _cb_p_route(struct snl_state *ss __unused, void *_target)
 }
 #undef _IN
 #undef _OUT
-SNL_DECLARE_PARSER_EXT(snl_rtm_route_parser, struct rtmsg, _fp_p_route, _nla_p_route, _cb_p_route);
+SNL_DECLARE_PARSER_EXT(snl_rtm_route_parser, sizeof(struct rtmsg),
+		sizeof(struct snl_parsed_route), _fp_p_route, _nla_p_route,
+		_cb_p_route);
 
 /* RTM_<NEW|DEL|GET>LINK message parser */
 struct snl_parsed_link {
@@ -299,7 +303,9 @@ _cb_p_neigh(struct snl_state *ss __unused, void *_target)
 }
 #undef _IN
 #undef _OUT
-SNL_DECLARE_PARSER_EXT(snl_rtm_neigh_parser, struct ndmsg, _fp_p_neigh_s, _nla_p_neigh_s, _cb_p_neigh);
+SNL_DECLARE_PARSER_EXT(snl_rtm_neigh_parser, sizeof(struct ndmsg),
+		sizeof(struct snl_parsed_neigh), _fp_p_neigh_s, _nla_p_neigh_s,
+		_cb_p_neigh);
 
 struct snl_parsed_addr {
 	uint8_t		ifa_family;
@@ -347,7 +353,9 @@ _cb_p_addr(struct snl_state *ss __unused, void *_target)
 }
 #undef _IN
 #undef _OUT
-SNL_DECLARE_PARSER_EXT(snl_rtm_addr_parser, struct ifaddrmsg, _fp_p_addr_s, _nla_p_addr_s, _cb_p_addr);
+SNL_DECLARE_PARSER_EXT(snl_rtm_addr_parser, sizeof(struct ifaddrmsg),
+		sizeof(struct snl_parsed_addr), _fp_p_addr_s, _nla_p_addr_s,
+		_cb_p_addr);
 
 struct snl_parsed_nhop {
 	uint32_t	nha_id;
@@ -397,7 +405,8 @@ _cb_p_nh(struct snl_state *ss __unused, void *_target)
 }
 #undef _IN
 #undef _OUT
-SNL_DECLARE_PARSER_EXT(snl_nhmsg_parser, struct nhmsg, _fp_p_nh, _nla_p_nh, _cb_p_nh);
+SNL_DECLARE_PARSER_EXT(snl_nhmsg_parser, sizeof(struct nhmsg),
+		sizeof(struct snl_parsed_nhop), _fp_p_nh, _nla_p_nh, _cb_p_nh);
 
 static const struct snl_hdr_parser *snl_all_route_parsers[] = {
 	&_metrics_mp_nh_parser, &_mpath_nh_parser, &_metrics_parser, &snl_rtm_route_parser,



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202305271114.34RBED8O007212>