xref: /openbmc/linux/tools/net/ynl/lib/ynl.c (revision 816ffd28002651a469e86d1118a225862e392ecd)
186878f14SJakub Kicinski // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
286878f14SJakub Kicinski #include <errno.h>
386878f14SJakub Kicinski #include <poll.h>
486878f14SJakub Kicinski #include <string.h>
586878f14SJakub Kicinski #include <stdlib.h>
686878f14SJakub Kicinski #include <linux/types.h>
786878f14SJakub Kicinski 
886878f14SJakub Kicinski #include <libmnl/libmnl.h>
986878f14SJakub Kicinski #include <linux/genetlink.h>
1086878f14SJakub Kicinski 
1186878f14SJakub Kicinski #include "ynl.h"
1286878f14SJakub Kicinski 
1386878f14SJakub Kicinski #define ARRAY_SIZE(arr)		(sizeof(arr) / sizeof(*arr))
1486878f14SJakub Kicinski 
1586878f14SJakub Kicinski #define __yerr_msg(yse, _msg...)					\
1686878f14SJakub Kicinski 	({								\
1786878f14SJakub Kicinski 		struct ynl_error *_yse = (yse);				\
1886878f14SJakub Kicinski 									\
1986878f14SJakub Kicinski 		if (_yse) {						\
2086878f14SJakub Kicinski 			snprintf(_yse->msg, sizeof(_yse->msg) - 1,  _msg); \
2186878f14SJakub Kicinski 			_yse->msg[sizeof(_yse->msg) - 1] = 0;		\
2286878f14SJakub Kicinski 		}							\
2386878f14SJakub Kicinski 	})
2486878f14SJakub Kicinski 
2586878f14SJakub Kicinski #define __yerr_code(yse, _code...)		\
2686878f14SJakub Kicinski 	({					\
2786878f14SJakub Kicinski 		struct ynl_error *_yse = (yse);	\
2886878f14SJakub Kicinski 						\
2986878f14SJakub Kicinski 		if (_yse) {			\
3086878f14SJakub Kicinski 			_yse->code = _code;	\
3186878f14SJakub Kicinski 		}				\
3286878f14SJakub Kicinski 	})
3386878f14SJakub Kicinski 
3486878f14SJakub Kicinski #define __yerr(yse, _code, _msg...)		\
3586878f14SJakub Kicinski 	({					\
3686878f14SJakub Kicinski 		__yerr_msg(yse, _msg);		\
3786878f14SJakub Kicinski 		__yerr_code(yse, _code);	\
3886878f14SJakub Kicinski 	})
3986878f14SJakub Kicinski 
4086878f14SJakub Kicinski #define __perr(yse, _msg)		__yerr(yse, errno, _msg)
4186878f14SJakub Kicinski 
4286878f14SJakub Kicinski #define yerr_msg(_ys, _msg...)		__yerr_msg(&(_ys)->err, _msg)
4386878f14SJakub Kicinski #define yerr(_ys, _code, _msg...)	__yerr(&(_ys)->err, _code, _msg)
4486878f14SJakub Kicinski #define perr(_ys, _msg)			__yerr(&(_ys)->err, errno, _msg)
4586878f14SJakub Kicinski 
4686878f14SJakub Kicinski /* -- Netlink boiler plate */
4786878f14SJakub Kicinski static int
ynl_err_walk_report_one(struct ynl_policy_nest * policy,unsigned int type,char * str,int str_sz,int * n)4886878f14SJakub Kicinski ynl_err_walk_report_one(struct ynl_policy_nest *policy, unsigned int type,
4986878f14SJakub Kicinski 			char *str, int str_sz, int *n)
5086878f14SJakub Kicinski {
5186878f14SJakub Kicinski 	if (!policy) {
5286878f14SJakub Kicinski 		if (*n < str_sz)
5386878f14SJakub Kicinski 			*n += snprintf(str, str_sz, "!policy");
5486878f14SJakub Kicinski 		return 1;
5586878f14SJakub Kicinski 	}
5686878f14SJakub Kicinski 
5786878f14SJakub Kicinski 	if (type > policy->max_attr) {
5886878f14SJakub Kicinski 		if (*n < str_sz)
5986878f14SJakub Kicinski 			*n += snprintf(str, str_sz, "!oob");
6086878f14SJakub Kicinski 		return 1;
6186878f14SJakub Kicinski 	}
6286878f14SJakub Kicinski 
6386878f14SJakub Kicinski 	if (!policy->table[type].name) {
6486878f14SJakub Kicinski 		if (*n < str_sz)
6586878f14SJakub Kicinski 			*n += snprintf(str, str_sz, "!name");
6686878f14SJakub Kicinski 		return 1;
6786878f14SJakub Kicinski 	}
6886878f14SJakub Kicinski 
6986878f14SJakub Kicinski 	if (*n < str_sz)
7086878f14SJakub Kicinski 		*n += snprintf(str, str_sz - *n,
7186878f14SJakub Kicinski 			       ".%s", policy->table[type].name);
7286878f14SJakub Kicinski 	return 0;
7386878f14SJakub Kicinski }
7486878f14SJakub Kicinski 
7586878f14SJakub Kicinski static int
ynl_err_walk(struct ynl_sock * ys,void * start,void * end,unsigned int off,struct ynl_policy_nest * policy,char * str,int str_sz,struct ynl_policy_nest ** nest_pol)7686878f14SJakub Kicinski ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off,
7786878f14SJakub Kicinski 	     struct ynl_policy_nest *policy, char *str, int str_sz,
7886878f14SJakub Kicinski 	     struct ynl_policy_nest **nest_pol)
7986878f14SJakub Kicinski {
8086878f14SJakub Kicinski 	unsigned int astart_off, aend_off;
8186878f14SJakub Kicinski 	const struct nlattr *attr;
8286878f14SJakub Kicinski 	unsigned int data_len;
8386878f14SJakub Kicinski 	unsigned int type;
8486878f14SJakub Kicinski 	bool found = false;
8586878f14SJakub Kicinski 	int n = 0;
8686878f14SJakub Kicinski 
8786878f14SJakub Kicinski 	if (!policy) {
8886878f14SJakub Kicinski 		if (n < str_sz)
8986878f14SJakub Kicinski 			n += snprintf(str, str_sz, "!policy");
9086878f14SJakub Kicinski 		return n;
9186878f14SJakub Kicinski 	}
9286878f14SJakub Kicinski 
9386878f14SJakub Kicinski 	data_len = end - start;
9486878f14SJakub Kicinski 
9586878f14SJakub Kicinski 	mnl_attr_for_each_payload(start, data_len) {
9686878f14SJakub Kicinski 		astart_off = (char *)attr - (char *)start;
9786878f14SJakub Kicinski 		aend_off = astart_off + mnl_attr_get_payload_len(attr);
9886878f14SJakub Kicinski 		if (aend_off <= off)
9986878f14SJakub Kicinski 			continue;
10086878f14SJakub Kicinski 
10186878f14SJakub Kicinski 		found = true;
10286878f14SJakub Kicinski 		break;
10386878f14SJakub Kicinski 	}
10486878f14SJakub Kicinski 	if (!found)
10586878f14SJakub Kicinski 		return 0;
10686878f14SJakub Kicinski 
10786878f14SJakub Kicinski 	off -= astart_off;
10886878f14SJakub Kicinski 
10986878f14SJakub Kicinski 	type = mnl_attr_get_type(attr);
11086878f14SJakub Kicinski 
11186878f14SJakub Kicinski 	if (ynl_err_walk_report_one(policy, type, str, str_sz, &n))
11286878f14SJakub Kicinski 		return n;
11386878f14SJakub Kicinski 
11486878f14SJakub Kicinski 	if (!off) {
11586878f14SJakub Kicinski 		if (nest_pol)
11686878f14SJakub Kicinski 			*nest_pol = policy->table[type].nest;
11786878f14SJakub Kicinski 		return n;
11886878f14SJakub Kicinski 	}
11986878f14SJakub Kicinski 
12086878f14SJakub Kicinski 	if (!policy->table[type].nest) {
12186878f14SJakub Kicinski 		if (n < str_sz)
12286878f14SJakub Kicinski 			n += snprintf(str, str_sz, "!nest");
12386878f14SJakub Kicinski 		return n;
12486878f14SJakub Kicinski 	}
12586878f14SJakub Kicinski 
12686878f14SJakub Kicinski 	off -= sizeof(struct nlattr);
12786878f14SJakub Kicinski 	start =  mnl_attr_get_payload(attr);
12886878f14SJakub Kicinski 	end = start + mnl_attr_get_payload_len(attr);
12986878f14SJakub Kicinski 
13086878f14SJakub Kicinski 	return n + ynl_err_walk(ys, start, end, off, policy->table[type].nest,
13186878f14SJakub Kicinski 				&str[n], str_sz - n, nest_pol);
13286878f14SJakub Kicinski }
13386878f14SJakub Kicinski 
13486878f14SJakub Kicinski #define NLMSGERR_ATTR_MISS_TYPE (NLMSGERR_ATTR_POLICY + 1)
13586878f14SJakub Kicinski #define NLMSGERR_ATTR_MISS_NEST (NLMSGERR_ATTR_POLICY + 2)
13686878f14SJakub Kicinski #define NLMSGERR_ATTR_MAX (NLMSGERR_ATTR_MAX + 2)
13786878f14SJakub Kicinski 
13886878f14SJakub Kicinski static int
ynl_ext_ack_check(struct ynl_sock * ys,const struct nlmsghdr * nlh,unsigned int hlen)13986878f14SJakub Kicinski ynl_ext_ack_check(struct ynl_sock *ys, const struct nlmsghdr *nlh,
14086878f14SJakub Kicinski 		  unsigned int hlen)
14186878f14SJakub Kicinski {
14286878f14SJakub Kicinski 	const struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
14386878f14SJakub Kicinski 	char miss_attr[sizeof(ys->err.msg)];
14486878f14SJakub Kicinski 	char bad_attr[sizeof(ys->err.msg)];
14586878f14SJakub Kicinski 	const struct nlattr *attr;
14686878f14SJakub Kicinski 	const char *str = NULL;
14786878f14SJakub Kicinski 
14886878f14SJakub Kicinski 	if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
14986878f14SJakub Kicinski 		return MNL_CB_OK;
15086878f14SJakub Kicinski 
15186878f14SJakub Kicinski 	mnl_attr_for_each(attr, nlh, hlen) {
15286878f14SJakub Kicinski 		unsigned int len, type;
15386878f14SJakub Kicinski 
15486878f14SJakub Kicinski 		len = mnl_attr_get_payload_len(attr);
15586878f14SJakub Kicinski 		type = mnl_attr_get_type(attr);
15686878f14SJakub Kicinski 
15786878f14SJakub Kicinski 		if (type > NLMSGERR_ATTR_MAX)
15886878f14SJakub Kicinski 			continue;
15986878f14SJakub Kicinski 
16086878f14SJakub Kicinski 		tb[type] = attr;
16186878f14SJakub Kicinski 
16286878f14SJakub Kicinski 		switch (type) {
16386878f14SJakub Kicinski 		case NLMSGERR_ATTR_OFFS:
16486878f14SJakub Kicinski 		case NLMSGERR_ATTR_MISS_TYPE:
16586878f14SJakub Kicinski 		case NLMSGERR_ATTR_MISS_NEST:
16686878f14SJakub Kicinski 			if (len != sizeof(__u32))
16786878f14SJakub Kicinski 				return MNL_CB_ERROR;
16886878f14SJakub Kicinski 			break;
16986878f14SJakub Kicinski 		case NLMSGERR_ATTR_MSG:
17086878f14SJakub Kicinski 			str = mnl_attr_get_payload(attr);
17186878f14SJakub Kicinski 			if (str[len - 1])
17286878f14SJakub Kicinski 				return MNL_CB_ERROR;
17386878f14SJakub Kicinski 			break;
17486878f14SJakub Kicinski 		default:
17586878f14SJakub Kicinski 			break;
17686878f14SJakub Kicinski 		}
17786878f14SJakub Kicinski 	}
17886878f14SJakub Kicinski 
17986878f14SJakub Kicinski 	bad_attr[0] = '\0';
18086878f14SJakub Kicinski 	miss_attr[0] = '\0';
18186878f14SJakub Kicinski 
18286878f14SJakub Kicinski 	if (tb[NLMSGERR_ATTR_OFFS]) {
18386878f14SJakub Kicinski 		unsigned int n, off;
18486878f14SJakub Kicinski 		void *start, *end;
18586878f14SJakub Kicinski 
18686878f14SJakub Kicinski 		ys->err.attr_offs = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
18786878f14SJakub Kicinski 
18886878f14SJakub Kicinski 		n = snprintf(bad_attr, sizeof(bad_attr), "%sbad attribute: ",
18986878f14SJakub Kicinski 			     str ? " (" : "");
19086878f14SJakub Kicinski 
19186878f14SJakub Kicinski 		start = mnl_nlmsg_get_payload_offset(ys->nlh,
19286878f14SJakub Kicinski 						     sizeof(struct genlmsghdr));
19386878f14SJakub Kicinski 		end = mnl_nlmsg_get_payload_tail(ys->nlh);
19486878f14SJakub Kicinski 
19586878f14SJakub Kicinski 		off = ys->err.attr_offs;
19686878f14SJakub Kicinski 		off -= sizeof(struct nlmsghdr);
19786878f14SJakub Kicinski 		off -= sizeof(struct genlmsghdr);
19886878f14SJakub Kicinski 
19986878f14SJakub Kicinski 		n += ynl_err_walk(ys, start, end, off, ys->req_policy,
20086878f14SJakub Kicinski 				  &bad_attr[n], sizeof(bad_attr) - n, NULL);
20186878f14SJakub Kicinski 
20286878f14SJakub Kicinski 		if (n >= sizeof(bad_attr))
20386878f14SJakub Kicinski 			n = sizeof(bad_attr) - 1;
20486878f14SJakub Kicinski 		bad_attr[n] = '\0';
20586878f14SJakub Kicinski 	}
20686878f14SJakub Kicinski 	if (tb[NLMSGERR_ATTR_MISS_TYPE]) {
20786878f14SJakub Kicinski 		struct ynl_policy_nest *nest_pol = NULL;
20886878f14SJakub Kicinski 		unsigned int n, off, type;
20986878f14SJakub Kicinski 		void *start, *end;
21086878f14SJakub Kicinski 		int n2;
21186878f14SJakub Kicinski 
21286878f14SJakub Kicinski 		type = mnl_attr_get_u32(tb[NLMSGERR_ATTR_MISS_TYPE]);
21386878f14SJakub Kicinski 
21486878f14SJakub Kicinski 		n = snprintf(miss_attr, sizeof(miss_attr), "%smissing attribute: ",
21586878f14SJakub Kicinski 			     bad_attr[0] ? ", " : (str ? " (" : ""));
21686878f14SJakub Kicinski 
21786878f14SJakub Kicinski 		start = mnl_nlmsg_get_payload_offset(ys->nlh,
21886878f14SJakub Kicinski 						     sizeof(struct genlmsghdr));
21986878f14SJakub Kicinski 		end = mnl_nlmsg_get_payload_tail(ys->nlh);
22086878f14SJakub Kicinski 
22186878f14SJakub Kicinski 		nest_pol = ys->req_policy;
22286878f14SJakub Kicinski 		if (tb[NLMSGERR_ATTR_MISS_NEST]) {
22386878f14SJakub Kicinski 			off = mnl_attr_get_u32(tb[NLMSGERR_ATTR_MISS_NEST]);
22486878f14SJakub Kicinski 			off -= sizeof(struct nlmsghdr);
22586878f14SJakub Kicinski 			off -= sizeof(struct genlmsghdr);
22686878f14SJakub Kicinski 
22786878f14SJakub Kicinski 			n += ynl_err_walk(ys, start, end, off, ys->req_policy,
22886878f14SJakub Kicinski 					  &miss_attr[n], sizeof(miss_attr) - n,
22986878f14SJakub Kicinski 					  &nest_pol);
23086878f14SJakub Kicinski 		}
23186878f14SJakub Kicinski 
23286878f14SJakub Kicinski 		n2 = 0;
23386878f14SJakub Kicinski 		ynl_err_walk_report_one(nest_pol, type, &miss_attr[n],
23486878f14SJakub Kicinski 					sizeof(miss_attr) - n, &n2);
23586878f14SJakub Kicinski 		n += n2;
23686878f14SJakub Kicinski 
23786878f14SJakub Kicinski 		if (n >= sizeof(miss_attr))
23886878f14SJakub Kicinski 			n = sizeof(miss_attr) - 1;
23986878f14SJakub Kicinski 		miss_attr[n] = '\0';
24086878f14SJakub Kicinski 	}
24186878f14SJakub Kicinski 
24286878f14SJakub Kicinski 	/* Implicitly depend on ys->err.code already set */
24386878f14SJakub Kicinski 	if (str)
24486878f14SJakub Kicinski 		yerr_msg(ys, "Kernel %s: '%s'%s%s%s",
24586878f14SJakub Kicinski 			 ys->err.code ? "error" : "warning",
24686878f14SJakub Kicinski 			 str, bad_attr, miss_attr,
24786878f14SJakub Kicinski 			 bad_attr[0] || miss_attr[0] ? ")" : "");
24886878f14SJakub Kicinski 	else if (bad_attr[0] || miss_attr[0])
24986878f14SJakub Kicinski 		yerr_msg(ys, "Kernel %s: %s%s",
25086878f14SJakub Kicinski 			 ys->err.code ? "error" : "warning",
25186878f14SJakub Kicinski 			 bad_attr, miss_attr);
25286878f14SJakub Kicinski 
25386878f14SJakub Kicinski 	return MNL_CB_OK;
25486878f14SJakub Kicinski }
25586878f14SJakub Kicinski 
ynl_cb_error(const struct nlmsghdr * nlh,void * data)25686878f14SJakub Kicinski static int ynl_cb_error(const struct nlmsghdr *nlh, void *data)
25786878f14SJakub Kicinski {
25886878f14SJakub Kicinski 	const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
25986878f14SJakub Kicinski 	struct ynl_parse_arg *yarg = data;
26086878f14SJakub Kicinski 	unsigned int hlen;
26186878f14SJakub Kicinski 	int code;
26286878f14SJakub Kicinski 
26386878f14SJakub Kicinski 	code = err->error >= 0 ? err->error : -err->error;
26486878f14SJakub Kicinski 	yarg->ys->err.code = code;
26586878f14SJakub Kicinski 	errno = code;
26686878f14SJakub Kicinski 
26786878f14SJakub Kicinski 	hlen = sizeof(*err);
26886878f14SJakub Kicinski 	if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
26986878f14SJakub Kicinski 		hlen += mnl_nlmsg_get_payload_len(&err->msg);
27086878f14SJakub Kicinski 
27186878f14SJakub Kicinski 	ynl_ext_ack_check(yarg->ys, nlh, hlen);
27286878f14SJakub Kicinski 
27386878f14SJakub Kicinski 	return code ? MNL_CB_ERROR : MNL_CB_STOP;
27486878f14SJakub Kicinski }
27586878f14SJakub Kicinski 
ynl_cb_done(const struct nlmsghdr * nlh,void * data)27686878f14SJakub Kicinski static int ynl_cb_done(const struct nlmsghdr *nlh, void *data)
27786878f14SJakub Kicinski {
27886878f14SJakub Kicinski 	struct ynl_parse_arg *yarg = data;
27986878f14SJakub Kicinski 	int err;
28086878f14SJakub Kicinski 
28186878f14SJakub Kicinski 	err = *(int *)NLMSG_DATA(nlh);
28286878f14SJakub Kicinski 	if (err < 0) {
28386878f14SJakub Kicinski 		yarg->ys->err.code = -err;
28486878f14SJakub Kicinski 		errno = -err;
28586878f14SJakub Kicinski 
28686878f14SJakub Kicinski 		ynl_ext_ack_check(yarg->ys, nlh, sizeof(int));
28786878f14SJakub Kicinski 
28886878f14SJakub Kicinski 		return MNL_CB_ERROR;
28986878f14SJakub Kicinski 	}
29086878f14SJakub Kicinski 	return MNL_CB_STOP;
29186878f14SJakub Kicinski }
29286878f14SJakub Kicinski 
ynl_cb_noop(const struct nlmsghdr * nlh,void * data)29386878f14SJakub Kicinski static int ynl_cb_noop(const struct nlmsghdr *nlh, void *data)
29486878f14SJakub Kicinski {
29586878f14SJakub Kicinski 	return MNL_CB_OK;
29686878f14SJakub Kicinski }
29786878f14SJakub Kicinski 
29886878f14SJakub Kicinski mnl_cb_t ynl_cb_array[NLMSG_MIN_TYPE] = {
29986878f14SJakub Kicinski 	[NLMSG_NOOP]	= ynl_cb_noop,
30086878f14SJakub Kicinski 	[NLMSG_ERROR]	= ynl_cb_error,
30186878f14SJakub Kicinski 	[NLMSG_DONE]	= ynl_cb_done,
30286878f14SJakub Kicinski 	[NLMSG_OVERRUN]	= ynl_cb_noop,
30386878f14SJakub Kicinski };
30486878f14SJakub Kicinski 
30586878f14SJakub Kicinski /* Attribute validation */
30686878f14SJakub Kicinski 
ynl_attr_validate(struct ynl_parse_arg * yarg,const struct nlattr * attr)30786878f14SJakub Kicinski int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr)
30886878f14SJakub Kicinski {
30986878f14SJakub Kicinski 	struct ynl_policy_attr *policy;
31086878f14SJakub Kicinski 	unsigned int type, len;
31186878f14SJakub Kicinski 	unsigned char *data;
31286878f14SJakub Kicinski 
31386878f14SJakub Kicinski 	data = mnl_attr_get_payload(attr);
31486878f14SJakub Kicinski 	len = mnl_attr_get_payload_len(attr);
31586878f14SJakub Kicinski 	type = mnl_attr_get_type(attr);
31686878f14SJakub Kicinski 	if (type > yarg->rsp_policy->max_attr) {
31786878f14SJakub Kicinski 		yerr(yarg->ys, YNL_ERROR_INTERNAL,
31886878f14SJakub Kicinski 		     "Internal error, validating unknown attribute");
31986878f14SJakub Kicinski 		return -1;
32086878f14SJakub Kicinski 	}
32186878f14SJakub Kicinski 
32286878f14SJakub Kicinski 	policy = &yarg->rsp_policy->table[type];
32386878f14SJakub Kicinski 
32486878f14SJakub Kicinski 	switch (policy->type) {
32586878f14SJakub Kicinski 	case YNL_PT_REJECT:
32686878f14SJakub Kicinski 		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
32786878f14SJakub Kicinski 		     "Rejected attribute (%s)", policy->name);
32886878f14SJakub Kicinski 		return -1;
32986878f14SJakub Kicinski 	case YNL_PT_IGNORE:
33086878f14SJakub Kicinski 		break;
33186878f14SJakub Kicinski 	case YNL_PT_U8:
33286878f14SJakub Kicinski 		if (len == sizeof(__u8))
33386878f14SJakub Kicinski 			break;
33486878f14SJakub Kicinski 		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
33586878f14SJakub Kicinski 		     "Invalid attribute (u8 %s)", policy->name);
33686878f14SJakub Kicinski 		return -1;
33786878f14SJakub Kicinski 	case YNL_PT_U16:
33886878f14SJakub Kicinski 		if (len == sizeof(__u16))
33986878f14SJakub Kicinski 			break;
34086878f14SJakub Kicinski 		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
34186878f14SJakub Kicinski 		     "Invalid attribute (u16 %s)", policy->name);
34286878f14SJakub Kicinski 		return -1;
34386878f14SJakub Kicinski 	case YNL_PT_U32:
34486878f14SJakub Kicinski 		if (len == sizeof(__u32))
34586878f14SJakub Kicinski 			break;
34686878f14SJakub Kicinski 		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
34786878f14SJakub Kicinski 		     "Invalid attribute (u32 %s)", policy->name);
34886878f14SJakub Kicinski 		return -1;
34986878f14SJakub Kicinski 	case YNL_PT_U64:
35086878f14SJakub Kicinski 		if (len == sizeof(__u64))
35186878f14SJakub Kicinski 			break;
35286878f14SJakub Kicinski 		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
35386878f14SJakub Kicinski 		     "Invalid attribute (u64 %s)", policy->name);
35486878f14SJakub Kicinski 		return -1;
35586878f14SJakub Kicinski 	case YNL_PT_FLAG:
35686878f14SJakub Kicinski 		/* Let flags grow into real attrs, why not.. */
35786878f14SJakub Kicinski 		break;
35886878f14SJakub Kicinski 	case YNL_PT_NEST:
35986878f14SJakub Kicinski 		if (!len || len >= sizeof(*attr))
36086878f14SJakub Kicinski 			break;
36186878f14SJakub Kicinski 		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
36286878f14SJakub Kicinski 		     "Invalid attribute (nest %s)", policy->name);
36386878f14SJakub Kicinski 		return -1;
36486878f14SJakub Kicinski 	case YNL_PT_BINARY:
36586878f14SJakub Kicinski 		if (!policy->len || len == policy->len)
36686878f14SJakub Kicinski 			break;
36786878f14SJakub Kicinski 		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
36886878f14SJakub Kicinski 		     "Invalid attribute (binary %s)", policy->name);
36986878f14SJakub Kicinski 		return -1;
37086878f14SJakub Kicinski 	case YNL_PT_NUL_STR:
37186878f14SJakub Kicinski 		if ((!policy->len || len <= policy->len) && !data[len - 1])
37286878f14SJakub Kicinski 			break;
37386878f14SJakub Kicinski 		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
37486878f14SJakub Kicinski 		     "Invalid attribute (string %s)", policy->name);
37586878f14SJakub Kicinski 		return -1;
37686878f14SJakub Kicinski 	default:
37786878f14SJakub Kicinski 		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
37886878f14SJakub Kicinski 		     "Invalid attribute (unknown %s)", policy->name);
37986878f14SJakub Kicinski 		return -1;
38086878f14SJakub Kicinski 	}
38186878f14SJakub Kicinski 
38286878f14SJakub Kicinski 	return 0;
38386878f14SJakub Kicinski }
38486878f14SJakub Kicinski 
38586878f14SJakub Kicinski /* Generic code */
38686878f14SJakub Kicinski 
ynl_err_reset(struct ynl_sock * ys)38786878f14SJakub Kicinski static void ynl_err_reset(struct ynl_sock *ys)
38886878f14SJakub Kicinski {
38986878f14SJakub Kicinski 	ys->err.code = 0;
39086878f14SJakub Kicinski 	ys->err.attr_offs = 0;
39186878f14SJakub Kicinski 	ys->err.msg[0] = 0;
39286878f14SJakub Kicinski }
39386878f14SJakub Kicinski 
ynl_msg_start(struct ynl_sock * ys,__u32 id,__u16 flags)39486878f14SJakub Kicinski struct nlmsghdr *ynl_msg_start(struct ynl_sock *ys, __u32 id, __u16 flags)
39586878f14SJakub Kicinski {
39686878f14SJakub Kicinski 	struct nlmsghdr *nlh;
39786878f14SJakub Kicinski 
39886878f14SJakub Kicinski 	ynl_err_reset(ys);
39986878f14SJakub Kicinski 
40086878f14SJakub Kicinski 	nlh = ys->nlh = mnl_nlmsg_put_header(ys->tx_buf);
40186878f14SJakub Kicinski 	nlh->nlmsg_type	= id;
40286878f14SJakub Kicinski 	nlh->nlmsg_flags = flags;
40386878f14SJakub Kicinski 	nlh->nlmsg_seq = ++ys->seq;
40486878f14SJakub Kicinski 
40586878f14SJakub Kicinski 	return nlh;
40686878f14SJakub Kicinski }
40786878f14SJakub Kicinski 
40886878f14SJakub Kicinski struct nlmsghdr *
ynl_gemsg_start(struct ynl_sock * ys,__u32 id,__u16 flags,__u8 cmd,__u8 version)40986878f14SJakub Kicinski ynl_gemsg_start(struct ynl_sock *ys, __u32 id, __u16 flags,
41086878f14SJakub Kicinski 		__u8 cmd, __u8 version)
41186878f14SJakub Kicinski {
41286878f14SJakub Kicinski 	struct genlmsghdr gehdr;
41386878f14SJakub Kicinski 	struct nlmsghdr *nlh;
41486878f14SJakub Kicinski 	void *data;
41586878f14SJakub Kicinski 
41686878f14SJakub Kicinski 	nlh = ynl_msg_start(ys, id, flags);
41786878f14SJakub Kicinski 
41886878f14SJakub Kicinski 	memset(&gehdr, 0, sizeof(gehdr));
41986878f14SJakub Kicinski 	gehdr.cmd = cmd;
42086878f14SJakub Kicinski 	gehdr.version = version;
42186878f14SJakub Kicinski 
42286878f14SJakub Kicinski 	data = mnl_nlmsg_put_extra_header(nlh, sizeof(gehdr));
42386878f14SJakub Kicinski 	memcpy(data, &gehdr, sizeof(gehdr));
42486878f14SJakub Kicinski 
42586878f14SJakub Kicinski 	return nlh;
42686878f14SJakub Kicinski }
42786878f14SJakub Kicinski 
ynl_msg_start_req(struct ynl_sock * ys,__u32 id)42886878f14SJakub Kicinski void ynl_msg_start_req(struct ynl_sock *ys, __u32 id)
42986878f14SJakub Kicinski {
43086878f14SJakub Kicinski 	ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK);
43186878f14SJakub Kicinski }
43286878f14SJakub Kicinski 
ynl_msg_start_dump(struct ynl_sock * ys,__u32 id)43386878f14SJakub Kicinski void ynl_msg_start_dump(struct ynl_sock *ys, __u32 id)
43486878f14SJakub Kicinski {
43586878f14SJakub Kicinski 	ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
43686878f14SJakub Kicinski }
43786878f14SJakub Kicinski 
43886878f14SJakub Kicinski struct nlmsghdr *
ynl_gemsg_start_req(struct ynl_sock * ys,__u32 id,__u8 cmd,__u8 version)43986878f14SJakub Kicinski ynl_gemsg_start_req(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version)
44086878f14SJakub Kicinski {
44186878f14SJakub Kicinski 	return ynl_gemsg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK, cmd, version);
44286878f14SJakub Kicinski }
44386878f14SJakub Kicinski 
44486878f14SJakub Kicinski struct nlmsghdr *
ynl_gemsg_start_dump(struct ynl_sock * ys,__u32 id,__u8 cmd,__u8 version)44586878f14SJakub Kicinski ynl_gemsg_start_dump(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version)
44686878f14SJakub Kicinski {
44786878f14SJakub Kicinski 	return ynl_gemsg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP,
44886878f14SJakub Kicinski 			       cmd, version);
44986878f14SJakub Kicinski }
45086878f14SJakub Kicinski 
ynl_recv_ack(struct ynl_sock * ys,int ret)45186878f14SJakub Kicinski int ynl_recv_ack(struct ynl_sock *ys, int ret)
45286878f14SJakub Kicinski {
45391addaf5SJakub Kicinski 	struct ynl_parse_arg yarg = { .ys = ys, };
45491addaf5SJakub Kicinski 
45586878f14SJakub Kicinski 	if (!ret) {
45686878f14SJakub Kicinski 		yerr(ys, YNL_ERROR_EXPECT_ACK,
45786878f14SJakub Kicinski 		     "Expecting an ACK but nothing received");
45886878f14SJakub Kicinski 		return -1;
45986878f14SJakub Kicinski 	}
46086878f14SJakub Kicinski 
46186878f14SJakub Kicinski 	ret = mnl_socket_recvfrom(ys->sock, ys->rx_buf, MNL_SOCKET_BUFFER_SIZE);
46286878f14SJakub Kicinski 	if (ret < 0) {
46386878f14SJakub Kicinski 		perr(ys, "Socket receive failed");
46486878f14SJakub Kicinski 		return ret;
46586878f14SJakub Kicinski 	}
46686878f14SJakub Kicinski 	return mnl_cb_run(ys->rx_buf, ret, ys->seq, ys->portid,
46791addaf5SJakub Kicinski 			  ynl_cb_null, &yarg);
46886878f14SJakub Kicinski }
46986878f14SJakub Kicinski 
ynl_cb_null(const struct nlmsghdr * nlh,void * data)47086878f14SJakub Kicinski int ynl_cb_null(const struct nlmsghdr *nlh, void *data)
47186878f14SJakub Kicinski {
47286878f14SJakub Kicinski 	struct ynl_parse_arg *yarg = data;
47386878f14SJakub Kicinski 
47486878f14SJakub Kicinski 	yerr(yarg->ys, YNL_ERROR_UNEXPECT_MSG,
47586878f14SJakub Kicinski 	     "Received a message when none were expected");
47686878f14SJakub Kicinski 
47786878f14SJakub Kicinski 	return MNL_CB_ERROR;
47886878f14SJakub Kicinski }
47986878f14SJakub Kicinski 
48086878f14SJakub Kicinski /* Init/fini and genetlink boiler plate */
48186878f14SJakub Kicinski static int
ynl_get_family_info_mcast(struct ynl_sock * ys,const struct nlattr * mcasts)48286878f14SJakub Kicinski ynl_get_family_info_mcast(struct ynl_sock *ys, const struct nlattr *mcasts)
48386878f14SJakub Kicinski {
48486878f14SJakub Kicinski 	const struct nlattr *entry, *attr;
48586878f14SJakub Kicinski 	unsigned int i;
48686878f14SJakub Kicinski 
48786878f14SJakub Kicinski 	mnl_attr_for_each_nested(attr, mcasts)
48886878f14SJakub Kicinski 		ys->n_mcast_groups++;
48986878f14SJakub Kicinski 
49086878f14SJakub Kicinski 	if (!ys->n_mcast_groups)
49186878f14SJakub Kicinski 		return 0;
49286878f14SJakub Kicinski 
49386878f14SJakub Kicinski 	ys->mcast_groups = calloc(ys->n_mcast_groups,
49486878f14SJakub Kicinski 				  sizeof(*ys->mcast_groups));
49586878f14SJakub Kicinski 	if (!ys->mcast_groups)
49686878f14SJakub Kicinski 		return MNL_CB_ERROR;
49786878f14SJakub Kicinski 
49886878f14SJakub Kicinski 	i = 0;
49986878f14SJakub Kicinski 	mnl_attr_for_each_nested(entry, mcasts) {
50086878f14SJakub Kicinski 		mnl_attr_for_each_nested(attr, entry) {
50186878f14SJakub Kicinski 			if (mnl_attr_get_type(attr) == CTRL_ATTR_MCAST_GRP_ID)
50286878f14SJakub Kicinski 				ys->mcast_groups[i].id = mnl_attr_get_u32(attr);
50386878f14SJakub Kicinski 			if (mnl_attr_get_type(attr) == CTRL_ATTR_MCAST_GRP_NAME) {
50486878f14SJakub Kicinski 				strncpy(ys->mcast_groups[i].name,
50586878f14SJakub Kicinski 					mnl_attr_get_str(attr),
50686878f14SJakub Kicinski 					GENL_NAMSIZ - 1);
50786878f14SJakub Kicinski 				ys->mcast_groups[i].name[GENL_NAMSIZ - 1] = 0;
50886878f14SJakub Kicinski 			}
50986878f14SJakub Kicinski 		}
510*dbc81b03SJakub Kicinski 		i++;
51186878f14SJakub Kicinski 	}
51286878f14SJakub Kicinski 
51386878f14SJakub Kicinski 	return 0;
51486878f14SJakub Kicinski }
51586878f14SJakub Kicinski 
ynl_get_family_info_cb(const struct nlmsghdr * nlh,void * data)51686878f14SJakub Kicinski static int ynl_get_family_info_cb(const struct nlmsghdr *nlh, void *data)
51786878f14SJakub Kicinski {
51886878f14SJakub Kicinski 	struct ynl_parse_arg *yarg = data;
51986878f14SJakub Kicinski 	struct ynl_sock *ys = yarg->ys;
52086878f14SJakub Kicinski 	const struct nlattr *attr;
52186878f14SJakub Kicinski 	bool found_id = true;
52286878f14SJakub Kicinski 
52386878f14SJakub Kicinski 	mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) {
52486878f14SJakub Kicinski 		if (mnl_attr_get_type(attr) == CTRL_ATTR_MCAST_GROUPS)
52586878f14SJakub Kicinski 			if (ynl_get_family_info_mcast(ys, attr))
52686878f14SJakub Kicinski 				return MNL_CB_ERROR;
52786878f14SJakub Kicinski 
52886878f14SJakub Kicinski 		if (mnl_attr_get_type(attr) != CTRL_ATTR_FAMILY_ID)
52986878f14SJakub Kicinski 			continue;
53086878f14SJakub Kicinski 
53186878f14SJakub Kicinski 		if (mnl_attr_get_payload_len(attr) != sizeof(__u16)) {
53286878f14SJakub Kicinski 			yerr(ys, YNL_ERROR_ATTR_INVALID, "Invalid family ID");
53386878f14SJakub Kicinski 			return MNL_CB_ERROR;
53486878f14SJakub Kicinski 		}
53586878f14SJakub Kicinski 
53686878f14SJakub Kicinski 		ys->family_id = mnl_attr_get_u16(attr);
53786878f14SJakub Kicinski 		found_id = true;
53886878f14SJakub Kicinski 	}
53986878f14SJakub Kicinski 
54086878f14SJakub Kicinski 	if (!found_id) {
54186878f14SJakub Kicinski 		yerr(ys, YNL_ERROR_ATTR_MISSING, "Family ID missing");
54286878f14SJakub Kicinski 		return MNL_CB_ERROR;
54386878f14SJakub Kicinski 	}
54486878f14SJakub Kicinski 	return MNL_CB_OK;
54586878f14SJakub Kicinski }
54686878f14SJakub Kicinski 
ynl_sock_read_family(struct ynl_sock * ys,const char * family_name)54786878f14SJakub Kicinski static int ynl_sock_read_family(struct ynl_sock *ys, const char *family_name)
54886878f14SJakub Kicinski {
54986878f14SJakub Kicinski 	struct ynl_parse_arg yarg = { .ys = ys, };
55086878f14SJakub Kicinski 	struct nlmsghdr *nlh;
55186878f14SJakub Kicinski 	int err;
55286878f14SJakub Kicinski 
55386878f14SJakub Kicinski 	nlh = ynl_gemsg_start_req(ys, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, 1);
55486878f14SJakub Kicinski 	mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name);
55586878f14SJakub Kicinski 
55686878f14SJakub Kicinski 	err = mnl_socket_sendto(ys->sock, nlh, nlh->nlmsg_len);
55786878f14SJakub Kicinski 	if (err < 0) {
55886878f14SJakub Kicinski 		perr(ys, "failed to request socket family info");
55986878f14SJakub Kicinski 		return err;
56086878f14SJakub Kicinski 	}
56186878f14SJakub Kicinski 
56286878f14SJakub Kicinski 	err = mnl_socket_recvfrom(ys->sock, ys->rx_buf, MNL_SOCKET_BUFFER_SIZE);
56386878f14SJakub Kicinski 	if (err <= 0) {
56486878f14SJakub Kicinski 		perr(ys, "failed to receive the socket family info");
56586878f14SJakub Kicinski 		return err;
56686878f14SJakub Kicinski 	}
56786878f14SJakub Kicinski 	err = mnl_cb_run2(ys->rx_buf, err, ys->seq, ys->portid,
56886878f14SJakub Kicinski 			  ynl_get_family_info_cb, &yarg,
56986878f14SJakub Kicinski 			  ynl_cb_array, ARRAY_SIZE(ynl_cb_array));
57086878f14SJakub Kicinski 	if (err < 0) {
57186878f14SJakub Kicinski 		free(ys->mcast_groups);
57286878f14SJakub Kicinski 		perr(ys, "failed to receive the socket family info - no such family?");
57386878f14SJakub Kicinski 		return err;
57486878f14SJakub Kicinski 	}
57586878f14SJakub Kicinski 
576a702e988SJakub Kicinski 	err = ynl_recv_ack(ys, err);
577a702e988SJakub Kicinski 	if (err < 0) {
578a702e988SJakub Kicinski 		free(ys->mcast_groups);
579a702e988SJakub Kicinski 		return err;
580a702e988SJakub Kicinski 	}
581a702e988SJakub Kicinski 
582a702e988SJakub Kicinski 	return 0;
58386878f14SJakub Kicinski }
58486878f14SJakub Kicinski 
58586878f14SJakub Kicinski struct ynl_sock *
ynl_sock_create(const struct ynl_family * yf,struct ynl_error * yse)58686878f14SJakub Kicinski ynl_sock_create(const struct ynl_family *yf, struct ynl_error *yse)
58786878f14SJakub Kicinski {
58886878f14SJakub Kicinski 	struct ynl_sock *ys;
58986878f14SJakub Kicinski 	int one = 1;
59086878f14SJakub Kicinski 
59186878f14SJakub Kicinski 	ys = malloc(sizeof(*ys) + 2 * MNL_SOCKET_BUFFER_SIZE);
59286878f14SJakub Kicinski 	if (!ys)
59386878f14SJakub Kicinski 		return NULL;
59486878f14SJakub Kicinski 	memset(ys, 0, sizeof(*ys));
59586878f14SJakub Kicinski 
59686878f14SJakub Kicinski 	ys->family = yf;
59786878f14SJakub Kicinski 	ys->tx_buf = &ys->raw_buf[0];
59886878f14SJakub Kicinski 	ys->rx_buf = &ys->raw_buf[MNL_SOCKET_BUFFER_SIZE];
59986878f14SJakub Kicinski 	ys->ntf_last_next = &ys->ntf_first;
60086878f14SJakub Kicinski 
60186878f14SJakub Kicinski 	ys->sock = mnl_socket_open(NETLINK_GENERIC);
60286878f14SJakub Kicinski 	if (!ys->sock) {
60386878f14SJakub Kicinski 		__perr(yse, "failed to create a netlink socket");
60486878f14SJakub Kicinski 		goto err_free_sock;
60586878f14SJakub Kicinski 	}
60686878f14SJakub Kicinski 
60786878f14SJakub Kicinski 	if (mnl_socket_setsockopt(ys->sock, NETLINK_CAP_ACK,
60886878f14SJakub Kicinski 				  &one, sizeof(one))) {
60986878f14SJakub Kicinski 		__perr(yse, "failed to enable netlink ACK");
61086878f14SJakub Kicinski 		goto err_close_sock;
61186878f14SJakub Kicinski 	}
61286878f14SJakub Kicinski 	if (mnl_socket_setsockopt(ys->sock, NETLINK_EXT_ACK,
61386878f14SJakub Kicinski 				  &one, sizeof(one))) {
61486878f14SJakub Kicinski 		__perr(yse, "failed to enable netlink ext ACK");
61586878f14SJakub Kicinski 		goto err_close_sock;
61686878f14SJakub Kicinski 	}
61786878f14SJakub Kicinski 
61886878f14SJakub Kicinski 	ys->seq = random();
61986878f14SJakub Kicinski 	ys->portid = mnl_socket_get_portid(ys->sock);
62086878f14SJakub Kicinski 
62186878f14SJakub Kicinski 	if (ynl_sock_read_family(ys, yf->name)) {
62286878f14SJakub Kicinski 		if (yse)
62386878f14SJakub Kicinski 			memcpy(yse, &ys->err, sizeof(*yse));
62486878f14SJakub Kicinski 		goto err_close_sock;
62586878f14SJakub Kicinski 	}
62686878f14SJakub Kicinski 
62786878f14SJakub Kicinski 	return ys;
62886878f14SJakub Kicinski 
62986878f14SJakub Kicinski err_close_sock:
63086878f14SJakub Kicinski 	mnl_socket_close(ys->sock);
63186878f14SJakub Kicinski err_free_sock:
63286878f14SJakub Kicinski 	free(ys);
63386878f14SJakub Kicinski 	return NULL;
63486878f14SJakub Kicinski }
63586878f14SJakub Kicinski 
ynl_sock_destroy(struct ynl_sock * ys)63686878f14SJakub Kicinski void ynl_sock_destroy(struct ynl_sock *ys)
63786878f14SJakub Kicinski {
63886878f14SJakub Kicinski 	struct ynl_ntf_base_type *ntf;
63986878f14SJakub Kicinski 
64086878f14SJakub Kicinski 	mnl_socket_close(ys->sock);
64186878f14SJakub Kicinski 	while ((ntf = ynl_ntf_dequeue(ys)))
64286878f14SJakub Kicinski 		ynl_ntf_free(ntf);
64386878f14SJakub Kicinski 	free(ys->mcast_groups);
64486878f14SJakub Kicinski 	free(ys);
64586878f14SJakub Kicinski }
64686878f14SJakub Kicinski 
64786878f14SJakub Kicinski /* YNL multicast handling */
64886878f14SJakub Kicinski 
ynl_ntf_free(struct ynl_ntf_base_type * ntf)64986878f14SJakub Kicinski void ynl_ntf_free(struct ynl_ntf_base_type *ntf)
65086878f14SJakub Kicinski {
65186878f14SJakub Kicinski 	ntf->free(ntf);
65286878f14SJakub Kicinski }
65386878f14SJakub Kicinski 
ynl_subscribe(struct ynl_sock * ys,const char * grp_name)65486878f14SJakub Kicinski int ynl_subscribe(struct ynl_sock *ys, const char *grp_name)
65586878f14SJakub Kicinski {
65686878f14SJakub Kicinski 	unsigned int i;
65786878f14SJakub Kicinski 	int err;
65886878f14SJakub Kicinski 
65986878f14SJakub Kicinski 	for (i = 0; i < ys->n_mcast_groups; i++)
66086878f14SJakub Kicinski 		if (!strcmp(ys->mcast_groups[i].name, grp_name))
66186878f14SJakub Kicinski 			break;
66286878f14SJakub Kicinski 	if (i == ys->n_mcast_groups) {
66386878f14SJakub Kicinski 		yerr(ys, ENOENT, "Multicast group '%s' not found", grp_name);
66486878f14SJakub Kicinski 		return -1;
66586878f14SJakub Kicinski 	}
66686878f14SJakub Kicinski 
66786878f14SJakub Kicinski 	err = mnl_socket_setsockopt(ys->sock, NETLINK_ADD_MEMBERSHIP,
66886878f14SJakub Kicinski 				    &ys->mcast_groups[i].id,
66986878f14SJakub Kicinski 				    sizeof(ys->mcast_groups[i].id));
67086878f14SJakub Kicinski 	if (err < 0) {
67186878f14SJakub Kicinski 		perr(ys, "Subscribing to multicast group failed");
67286878f14SJakub Kicinski 		return -1;
67386878f14SJakub Kicinski 	}
67486878f14SJakub Kicinski 
67586878f14SJakub Kicinski 	return 0;
67686878f14SJakub Kicinski }
67786878f14SJakub Kicinski 
ynl_socket_get_fd(struct ynl_sock * ys)67886878f14SJakub Kicinski int ynl_socket_get_fd(struct ynl_sock *ys)
67986878f14SJakub Kicinski {
68086878f14SJakub Kicinski 	return mnl_socket_get_fd(ys->sock);
68186878f14SJakub Kicinski }
68286878f14SJakub Kicinski 
ynl_ntf_dequeue(struct ynl_sock * ys)68386878f14SJakub Kicinski struct ynl_ntf_base_type *ynl_ntf_dequeue(struct ynl_sock *ys)
68486878f14SJakub Kicinski {
68586878f14SJakub Kicinski 	struct ynl_ntf_base_type *ntf;
68686878f14SJakub Kicinski 
68786878f14SJakub Kicinski 	if (!ynl_has_ntf(ys))
68886878f14SJakub Kicinski 		return NULL;
68986878f14SJakub Kicinski 
69086878f14SJakub Kicinski 	ntf = ys->ntf_first;
69186878f14SJakub Kicinski 	ys->ntf_first = ntf->next;
69286878f14SJakub Kicinski 	if (ys->ntf_last_next == &ntf->next)
69386878f14SJakub Kicinski 		ys->ntf_last_next = &ys->ntf_first;
69486878f14SJakub Kicinski 
69586878f14SJakub Kicinski 	return ntf;
69686878f14SJakub Kicinski }
69786878f14SJakub Kicinski 
ynl_ntf_parse(struct ynl_sock * ys,const struct nlmsghdr * nlh)69886878f14SJakub Kicinski static int ynl_ntf_parse(struct ynl_sock *ys, const struct nlmsghdr *nlh)
69986878f14SJakub Kicinski {
70086878f14SJakub Kicinski 	struct ynl_parse_arg yarg = { .ys = ys, };
70186878f14SJakub Kicinski 	const struct ynl_ntf_info *info;
70286878f14SJakub Kicinski 	struct ynl_ntf_base_type *rsp;
70386878f14SJakub Kicinski 	struct genlmsghdr *gehdr;
70486878f14SJakub Kicinski 	int ret;
70586878f14SJakub Kicinski 
70686878f14SJakub Kicinski 	gehdr = mnl_nlmsg_get_payload(nlh);
70786878f14SJakub Kicinski 	if (gehdr->cmd >= ys->family->ntf_info_size)
70886878f14SJakub Kicinski 		return MNL_CB_ERROR;
70986878f14SJakub Kicinski 	info = &ys->family->ntf_info[gehdr->cmd];
71086878f14SJakub Kicinski 	if (!info->cb)
71186878f14SJakub Kicinski 		return MNL_CB_ERROR;
71286878f14SJakub Kicinski 
71386878f14SJakub Kicinski 	rsp = calloc(1, info->alloc_sz);
71486878f14SJakub Kicinski 	rsp->free = info->free;
71586878f14SJakub Kicinski 	yarg.data = rsp->data;
71686878f14SJakub Kicinski 	yarg.rsp_policy = info->policy;
71786878f14SJakub Kicinski 
71886878f14SJakub Kicinski 	ret = info->cb(nlh, &yarg);
71986878f14SJakub Kicinski 	if (ret <= MNL_CB_STOP)
72086878f14SJakub Kicinski 		goto err_free;
72186878f14SJakub Kicinski 
72286878f14SJakub Kicinski 	rsp->family = nlh->nlmsg_type;
72386878f14SJakub Kicinski 	rsp->cmd = gehdr->cmd;
72486878f14SJakub Kicinski 
72586878f14SJakub Kicinski 	*ys->ntf_last_next = rsp;
72686878f14SJakub Kicinski 	ys->ntf_last_next = &rsp->next;
72786878f14SJakub Kicinski 
72886878f14SJakub Kicinski 	return MNL_CB_OK;
72986878f14SJakub Kicinski 
73086878f14SJakub Kicinski err_free:
73186878f14SJakub Kicinski 	info->free(rsp);
73286878f14SJakub Kicinski 	return MNL_CB_ERROR;
73386878f14SJakub Kicinski }
73486878f14SJakub Kicinski 
ynl_ntf_trampoline(const struct nlmsghdr * nlh,void * data)73586878f14SJakub Kicinski static int ynl_ntf_trampoline(const struct nlmsghdr *nlh, void *data)
73686878f14SJakub Kicinski {
73791addaf5SJakub Kicinski 	struct ynl_parse_arg *yarg = data;
73891addaf5SJakub Kicinski 
73991addaf5SJakub Kicinski 	return ynl_ntf_parse(yarg->ys, nlh);
74086878f14SJakub Kicinski }
74186878f14SJakub Kicinski 
ynl_ntf_check(struct ynl_sock * ys)74286878f14SJakub Kicinski int ynl_ntf_check(struct ynl_sock *ys)
74386878f14SJakub Kicinski {
74491addaf5SJakub Kicinski 	struct ynl_parse_arg yarg = { .ys = ys, };
74586878f14SJakub Kicinski 	ssize_t len;
74686878f14SJakub Kicinski 	int err;
74786878f14SJakub Kicinski 
74886878f14SJakub Kicinski 	do {
74986878f14SJakub Kicinski 		/* libmnl doesn't let us pass flags to the recv to make
75086878f14SJakub Kicinski 		 * it non-blocking so we need to poll() or peek() :|
75186878f14SJakub Kicinski 		 */
75286878f14SJakub Kicinski 		struct pollfd pfd = { };
75386878f14SJakub Kicinski 
75486878f14SJakub Kicinski 		pfd.fd = mnl_socket_get_fd(ys->sock);
75586878f14SJakub Kicinski 		pfd.events = POLLIN;
75686878f14SJakub Kicinski 		err = poll(&pfd, 1, 1);
75786878f14SJakub Kicinski 		if (err < 1)
75886878f14SJakub Kicinski 			return err;
75986878f14SJakub Kicinski 
76086878f14SJakub Kicinski 		len = mnl_socket_recvfrom(ys->sock, ys->rx_buf,
76186878f14SJakub Kicinski 					  MNL_SOCKET_BUFFER_SIZE);
76286878f14SJakub Kicinski 		if (len < 0)
76386878f14SJakub Kicinski 			return len;
76486878f14SJakub Kicinski 
76586878f14SJakub Kicinski 		err = mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid,
76691addaf5SJakub Kicinski 				  ynl_ntf_trampoline, &yarg,
76786878f14SJakub Kicinski 				  ynl_cb_array, NLMSG_MIN_TYPE);
76886878f14SJakub Kicinski 		if (err < 0)
76986878f14SJakub Kicinski 			return err;
77086878f14SJakub Kicinski 	} while (err > 0);
77186878f14SJakub Kicinski 
77286878f14SJakub Kicinski 	return 0;
77386878f14SJakub Kicinski }
77486878f14SJakub Kicinski 
77586878f14SJakub Kicinski /* YNL specific helpers used by the auto-generated code */
77686878f14SJakub Kicinski 
77786878f14SJakub Kicinski struct ynl_dump_list_type *YNL_LIST_END = (void *)(0xb4d123);
77886878f14SJakub Kicinski 
ynl_error_unknown_notification(struct ynl_sock * ys,__u8 cmd)77986878f14SJakub Kicinski void ynl_error_unknown_notification(struct ynl_sock *ys, __u8 cmd)
78086878f14SJakub Kicinski {
78186878f14SJakub Kicinski 	yerr(ys, YNL_ERROR_UNKNOWN_NTF,
78286878f14SJakub Kicinski 	     "Unknown notification message type '%d'", cmd);
78386878f14SJakub Kicinski }
78486878f14SJakub Kicinski 
ynl_error_parse(struct ynl_parse_arg * yarg,const char * msg)78586878f14SJakub Kicinski int ynl_error_parse(struct ynl_parse_arg *yarg, const char *msg)
78686878f14SJakub Kicinski {
78786878f14SJakub Kicinski 	yerr(yarg->ys, YNL_ERROR_INV_RESP, "Error parsing response: %s", msg);
78886878f14SJakub Kicinski 	return MNL_CB_ERROR;
78986878f14SJakub Kicinski }
79086878f14SJakub Kicinski 
79186878f14SJakub Kicinski static int
ynl_check_alien(struct ynl_sock * ys,const struct nlmsghdr * nlh,__u32 rsp_cmd)79286878f14SJakub Kicinski ynl_check_alien(struct ynl_sock *ys, const struct nlmsghdr *nlh, __u32 rsp_cmd)
79386878f14SJakub Kicinski {
79486878f14SJakub Kicinski 	struct genlmsghdr *gehdr;
79586878f14SJakub Kicinski 
79686878f14SJakub Kicinski 	if (mnl_nlmsg_get_payload_len(nlh) < sizeof(*gehdr)) {
79786878f14SJakub Kicinski 		yerr(ys, YNL_ERROR_INV_RESP,
79886878f14SJakub Kicinski 		     "Kernel responded with truncated message");
79986878f14SJakub Kicinski 		return -1;
80086878f14SJakub Kicinski 	}
80186878f14SJakub Kicinski 
80286878f14SJakub Kicinski 	gehdr = mnl_nlmsg_get_payload(nlh);
80386878f14SJakub Kicinski 	if (gehdr->cmd != rsp_cmd)
80486878f14SJakub Kicinski 		return ynl_ntf_parse(ys, nlh);
80586878f14SJakub Kicinski 
80686878f14SJakub Kicinski 	return 0;
80786878f14SJakub Kicinski }
80886878f14SJakub Kicinski 
ynl_req_trampoline(const struct nlmsghdr * nlh,void * data)80986878f14SJakub Kicinski static int ynl_req_trampoline(const struct nlmsghdr *nlh, void *data)
81086878f14SJakub Kicinski {
81186878f14SJakub Kicinski 	struct ynl_req_state *yrs = data;
81286878f14SJakub Kicinski 	int ret;
81386878f14SJakub Kicinski 
81486878f14SJakub Kicinski 	ret = ynl_check_alien(yrs->yarg.ys, nlh, yrs->rsp_cmd);
81586878f14SJakub Kicinski 	if (ret)
81686878f14SJakub Kicinski 		return ret < 0 ? MNL_CB_ERROR : MNL_CB_OK;
81786878f14SJakub Kicinski 
81886878f14SJakub Kicinski 	return yrs->cb(nlh, &yrs->yarg);
81986878f14SJakub Kicinski }
82086878f14SJakub Kicinski 
ynl_exec(struct ynl_sock * ys,struct nlmsghdr * req_nlh,struct ynl_req_state * yrs)82186878f14SJakub Kicinski int ynl_exec(struct ynl_sock *ys, struct nlmsghdr *req_nlh,
82286878f14SJakub Kicinski 	     struct ynl_req_state *yrs)
82386878f14SJakub Kicinski {
82486878f14SJakub Kicinski 	ssize_t len;
82586878f14SJakub Kicinski 	int err;
82686878f14SJakub Kicinski 
82786878f14SJakub Kicinski 	err = mnl_socket_sendto(ys->sock, req_nlh, req_nlh->nlmsg_len);
82886878f14SJakub Kicinski 	if (err < 0)
82986878f14SJakub Kicinski 		return err;
83086878f14SJakub Kicinski 
83186878f14SJakub Kicinski 	do {
83286878f14SJakub Kicinski 		len = mnl_socket_recvfrom(ys->sock, ys->rx_buf,
83386878f14SJakub Kicinski 					  MNL_SOCKET_BUFFER_SIZE);
83486878f14SJakub Kicinski 		if (len < 0)
83586878f14SJakub Kicinski 			return len;
83686878f14SJakub Kicinski 
83786878f14SJakub Kicinski 		err = mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid,
83886878f14SJakub Kicinski 				  ynl_req_trampoline, yrs,
83986878f14SJakub Kicinski 				  ynl_cb_array, NLMSG_MIN_TYPE);
84086878f14SJakub Kicinski 		if (err < 0)
84186878f14SJakub Kicinski 			return err;
84286878f14SJakub Kicinski 	} while (err > 0);
84386878f14SJakub Kicinski 
84486878f14SJakub Kicinski 	return 0;
84586878f14SJakub Kicinski }
84686878f14SJakub Kicinski 
ynl_dump_trampoline(const struct nlmsghdr * nlh,void * data)84786878f14SJakub Kicinski static int ynl_dump_trampoline(const struct nlmsghdr *nlh, void *data)
84886878f14SJakub Kicinski {
84986878f14SJakub Kicinski 	struct ynl_dump_state *ds = data;
85086878f14SJakub Kicinski 	struct ynl_dump_list_type *obj;
85186878f14SJakub Kicinski 	struct ynl_parse_arg yarg = {};
85286878f14SJakub Kicinski 	int ret;
85386878f14SJakub Kicinski 
85486878f14SJakub Kicinski 	ret = ynl_check_alien(ds->ys, nlh, ds->rsp_cmd);
85586878f14SJakub Kicinski 	if (ret)
85686878f14SJakub Kicinski 		return ret < 0 ? MNL_CB_ERROR : MNL_CB_OK;
85786878f14SJakub Kicinski 
85886878f14SJakub Kicinski 	obj = calloc(1, ds->alloc_sz);
85986878f14SJakub Kicinski 	if (!obj)
86086878f14SJakub Kicinski 		return MNL_CB_ERROR;
86186878f14SJakub Kicinski 
86286878f14SJakub Kicinski 	if (!ds->first)
86386878f14SJakub Kicinski 		ds->first = obj;
86486878f14SJakub Kicinski 	if (ds->last)
86586878f14SJakub Kicinski 		ds->last->next = obj;
86686878f14SJakub Kicinski 	ds->last = obj;
86786878f14SJakub Kicinski 
86886878f14SJakub Kicinski 	yarg.ys = ds->ys;
86986878f14SJakub Kicinski 	yarg.rsp_policy = ds->rsp_policy;
87086878f14SJakub Kicinski 	yarg.data = &obj->data;
87186878f14SJakub Kicinski 
87286878f14SJakub Kicinski 	return ds->cb(nlh, &yarg);
87386878f14SJakub Kicinski }
87486878f14SJakub Kicinski 
ynl_dump_end(struct ynl_dump_state * ds)87586878f14SJakub Kicinski static void *ynl_dump_end(struct ynl_dump_state *ds)
87686878f14SJakub Kicinski {
87786878f14SJakub Kicinski 	if (!ds->first)
87886878f14SJakub Kicinski 		return YNL_LIST_END;
87986878f14SJakub Kicinski 
88086878f14SJakub Kicinski 	ds->last->next = YNL_LIST_END;
88186878f14SJakub Kicinski 	return ds->first;
88286878f14SJakub Kicinski }
88386878f14SJakub Kicinski 
ynl_exec_dump(struct ynl_sock * ys,struct nlmsghdr * req_nlh,struct ynl_dump_state * yds)88486878f14SJakub Kicinski int ynl_exec_dump(struct ynl_sock *ys, struct nlmsghdr *req_nlh,
88586878f14SJakub Kicinski 		  struct ynl_dump_state *yds)
88686878f14SJakub Kicinski {
88786878f14SJakub Kicinski 	ssize_t len;
88886878f14SJakub Kicinski 	int err;
88986878f14SJakub Kicinski 
89086878f14SJakub Kicinski 	err = mnl_socket_sendto(ys->sock, req_nlh, req_nlh->nlmsg_len);
89186878f14SJakub Kicinski 	if (err < 0)
89286878f14SJakub Kicinski 		return err;
89386878f14SJakub Kicinski 
89486878f14SJakub Kicinski 	do {
89586878f14SJakub Kicinski 		len = mnl_socket_recvfrom(ys->sock, ys->rx_buf,
89686878f14SJakub Kicinski 					  MNL_SOCKET_BUFFER_SIZE);
89786878f14SJakub Kicinski 		if (len < 0)
89886878f14SJakub Kicinski 			goto err_close_list;
89986878f14SJakub Kicinski 
90086878f14SJakub Kicinski 		err = mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid,
90186878f14SJakub Kicinski 				  ynl_dump_trampoline, yds,
90286878f14SJakub Kicinski 				  ynl_cb_array, NLMSG_MIN_TYPE);
90386878f14SJakub Kicinski 		if (err < 0)
90486878f14SJakub Kicinski 			goto err_close_list;
90586878f14SJakub Kicinski 	} while (err > 0);
90686878f14SJakub Kicinski 
90786878f14SJakub Kicinski 	yds->first = ynl_dump_end(yds);
90886878f14SJakub Kicinski 	return 0;
90986878f14SJakub Kicinski 
91086878f14SJakub Kicinski err_close_list:
91186878f14SJakub Kicinski 	yds->first = ynl_dump_end(yds);
91286878f14SJakub Kicinski 	return -1;
91386878f14SJakub Kicinski }
914