Linux Audio

Check our new training course

Yocto / OpenEmbedded training

Feb 10-13, 2025
Register
Loading...
Note: File does not exist in v4.10.11.
  1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
  2#include <errno.h>
  3#include <poll.h>
  4#include <string.h>
  5#include <stdlib.h>
  6#include <linux/types.h>
  7
  8#include <libmnl/libmnl.h>
  9#include <linux/genetlink.h>
 10
 11#include "ynl.h"
 12
 13#define ARRAY_SIZE(arr)		(sizeof(arr) / sizeof(*arr))
 14
 15#define __yerr_msg(yse, _msg...)					\
 16	({								\
 17		struct ynl_error *_yse = (yse);				\
 18									\
 19		if (_yse) {						\
 20			snprintf(_yse->msg, sizeof(_yse->msg) - 1,  _msg); \
 21			_yse->msg[sizeof(_yse->msg) - 1] = 0;		\
 22		}							\
 23	})
 24
 25#define __yerr_code(yse, _code...)		\
 26	({					\
 27		struct ynl_error *_yse = (yse);	\
 28						\
 29		if (_yse) {			\
 30			_yse->code = _code;	\
 31		}				\
 32	})
 33
 34#define __yerr(yse, _code, _msg...)		\
 35	({					\
 36		__yerr_msg(yse, _msg);		\
 37		__yerr_code(yse, _code);	\
 38	})
 39
 40#define __perr(yse, _msg)		__yerr(yse, errno, _msg)
 41
 42#define yerr_msg(_ys, _msg...)		__yerr_msg(&(_ys)->err, _msg)
 43#define yerr(_ys, _code, _msg...)	__yerr(&(_ys)->err, _code, _msg)
 44#define perr(_ys, _msg)			__yerr(&(_ys)->err, errno, _msg)
 45
 46/* -- Netlink boiler plate */
 47static int
 48ynl_err_walk_report_one(struct ynl_policy_nest *policy, unsigned int type,
 49			char *str, int str_sz, int *n)
 50{
 51	if (!policy) {
 52		if (*n < str_sz)
 53			*n += snprintf(str, str_sz, "!policy");
 54		return 1;
 55	}
 56
 57	if (type > policy->max_attr) {
 58		if (*n < str_sz)
 59			*n += snprintf(str, str_sz, "!oob");
 60		return 1;
 61	}
 62
 63	if (!policy->table[type].name) {
 64		if (*n < str_sz)
 65			*n += snprintf(str, str_sz, "!name");
 66		return 1;
 67	}
 68
 69	if (*n < str_sz)
 70		*n += snprintf(str, str_sz - *n,
 71			       ".%s", policy->table[type].name);
 72	return 0;
 73}
 74
 75static int
 76ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off,
 77	     struct ynl_policy_nest *policy, char *str, int str_sz,
 78	     struct ynl_policy_nest **nest_pol)
 79{
 80	unsigned int astart_off, aend_off;
 81	const struct nlattr *attr;
 82	unsigned int data_len;
 83	unsigned int type;
 84	bool found = false;
 85	int n = 0;
 86
 87	if (!policy) {
 88		if (n < str_sz)
 89			n += snprintf(str, str_sz, "!policy");
 90		return n;
 91	}
 92
 93	data_len = end - start;
 94
 95	mnl_attr_for_each_payload(start, data_len) {
 96		astart_off = (char *)attr - (char *)start;
 97		aend_off = astart_off + mnl_attr_get_payload_len(attr);
 98		if (aend_off <= off)
 99			continue;
100
101		found = true;
102		break;
103	}
104	if (!found)
105		return 0;
106
107	off -= astart_off;
108
109	type = mnl_attr_get_type(attr);
110
111	if (ynl_err_walk_report_one(policy, type, str, str_sz, &n))
112		return n;
113
114	if (!off) {
115		if (nest_pol)
116			*nest_pol = policy->table[type].nest;
117		return n;
118	}
119
120	if (!policy->table[type].nest) {
121		if (n < str_sz)
122			n += snprintf(str, str_sz, "!nest");
123		return n;
124	}
125
126	off -= sizeof(struct nlattr);
127	start =  mnl_attr_get_payload(attr);
128	end = start + mnl_attr_get_payload_len(attr);
129
130	return n + ynl_err_walk(ys, start, end, off, policy->table[type].nest,
131				&str[n], str_sz - n, nest_pol);
132}
133
134#define NLMSGERR_ATTR_MISS_TYPE (NLMSGERR_ATTR_POLICY + 1)
135#define NLMSGERR_ATTR_MISS_NEST (NLMSGERR_ATTR_POLICY + 2)
136#define NLMSGERR_ATTR_MAX (NLMSGERR_ATTR_MAX + 2)
137
138static int
139ynl_ext_ack_check(struct ynl_sock *ys, const struct nlmsghdr *nlh,
140		  unsigned int hlen)
141{
142	const struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
143	char miss_attr[sizeof(ys->err.msg)];
144	char bad_attr[sizeof(ys->err.msg)];
145	const struct nlattr *attr;
146	const char *str = NULL;
147
148	if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) {
149		yerr_msg(ys, "%s", strerror(ys->err.code));
150		return MNL_CB_OK;
151	}
152
153	mnl_attr_for_each(attr, nlh, hlen) {
154		unsigned int len, type;
155
156		len = mnl_attr_get_payload_len(attr);
157		type = mnl_attr_get_type(attr);
158
159		if (type > NLMSGERR_ATTR_MAX)
160			continue;
161
162		tb[type] = attr;
163
164		switch (type) {
165		case NLMSGERR_ATTR_OFFS:
166		case NLMSGERR_ATTR_MISS_TYPE:
167		case NLMSGERR_ATTR_MISS_NEST:
168			if (len != sizeof(__u32))
169				return MNL_CB_ERROR;
170			break;
171		case NLMSGERR_ATTR_MSG:
172			str = mnl_attr_get_payload(attr);
173			if (str[len - 1])
174				return MNL_CB_ERROR;
175			break;
176		default:
177			break;
178		}
179	}
180
181	bad_attr[0] = '\0';
182	miss_attr[0] = '\0';
183
184	if (tb[NLMSGERR_ATTR_OFFS]) {
185		unsigned int n, off;
186		void *start, *end;
187
188		ys->err.attr_offs = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
189
190		n = snprintf(bad_attr, sizeof(bad_attr), "%sbad attribute: ",
191			     str ? " (" : "");
192
193		start = mnl_nlmsg_get_payload_offset(ys->nlh,
194						     ys->family->hdr_len);
195		end = mnl_nlmsg_get_payload_tail(ys->nlh);
196
197		off = ys->err.attr_offs;
198		off -= sizeof(struct nlmsghdr);
199		off -= ys->family->hdr_len;
200
201		n += ynl_err_walk(ys, start, end, off, ys->req_policy,
202				  &bad_attr[n], sizeof(bad_attr) - n, NULL);
203
204		if (n >= sizeof(bad_attr))
205			n = sizeof(bad_attr) - 1;
206		bad_attr[n] = '\0';
207	}
208	if (tb[NLMSGERR_ATTR_MISS_TYPE]) {
209		struct ynl_policy_nest *nest_pol = NULL;
210		unsigned int n, off, type;
211		void *start, *end;
212		int n2;
213
214		type = mnl_attr_get_u32(tb[NLMSGERR_ATTR_MISS_TYPE]);
215
216		n = snprintf(miss_attr, sizeof(miss_attr), "%smissing attribute: ",
217			     bad_attr[0] ? ", " : (str ? " (" : ""));
218
219		start = mnl_nlmsg_get_payload_offset(ys->nlh,
220						     ys->family->hdr_len);
221		end = mnl_nlmsg_get_payload_tail(ys->nlh);
222
223		nest_pol = ys->req_policy;
224		if (tb[NLMSGERR_ATTR_MISS_NEST]) {
225			off = mnl_attr_get_u32(tb[NLMSGERR_ATTR_MISS_NEST]);
226			off -= sizeof(struct nlmsghdr);
227			off -= ys->family->hdr_len;
228
229			n += ynl_err_walk(ys, start, end, off, ys->req_policy,
230					  &miss_attr[n], sizeof(miss_attr) - n,
231					  &nest_pol);
232		}
233
234		n2 = 0;
235		ynl_err_walk_report_one(nest_pol, type, &miss_attr[n],
236					sizeof(miss_attr) - n, &n2);
237		n += n2;
238
239		if (n >= sizeof(miss_attr))
240			n = sizeof(miss_attr) - 1;
241		miss_attr[n] = '\0';
242	}
243
244	/* Implicitly depend on ys->err.code already set */
245	if (str)
246		yerr_msg(ys, "Kernel %s: '%s'%s%s%s",
247			 ys->err.code ? "error" : "warning",
248			 str, bad_attr, miss_attr,
249			 bad_attr[0] || miss_attr[0] ? ")" : "");
250	else if (bad_attr[0] || miss_attr[0])
251		yerr_msg(ys, "Kernel %s: %s%s",
252			 ys->err.code ? "error" : "warning",
253			 bad_attr, miss_attr);
254	else
255		yerr_msg(ys, "%s", strerror(ys->err.code));
256
257	return MNL_CB_OK;
258}
259
260static int ynl_cb_error(const struct nlmsghdr *nlh, void *data)
261{
262	const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
263	struct ynl_parse_arg *yarg = data;
264	unsigned int hlen;
265	int code;
266
267	code = err->error >= 0 ? err->error : -err->error;
268	yarg->ys->err.code = code;
269	errno = code;
270
271	hlen = sizeof(*err);
272	if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
273		hlen += mnl_nlmsg_get_payload_len(&err->msg);
274
275	ynl_ext_ack_check(yarg->ys, nlh, hlen);
276
277	return code ? MNL_CB_ERROR : MNL_CB_STOP;
278}
279
280static int ynl_cb_done(const struct nlmsghdr *nlh, void *data)
281{
282	struct ynl_parse_arg *yarg = data;
283	int err;
284
285	err = *(int *)NLMSG_DATA(nlh);
286	if (err < 0) {
287		yarg->ys->err.code = -err;
288		errno = -err;
289
290		ynl_ext_ack_check(yarg->ys, nlh, sizeof(int));
291
292		return MNL_CB_ERROR;
293	}
294	return MNL_CB_STOP;
295}
296
297static int ynl_cb_noop(const struct nlmsghdr *nlh, void *data)
298{
299	return MNL_CB_OK;
300}
301
302mnl_cb_t ynl_cb_array[NLMSG_MIN_TYPE] = {
303	[NLMSG_NOOP]	= ynl_cb_noop,
304	[NLMSG_ERROR]	= ynl_cb_error,
305	[NLMSG_DONE]	= ynl_cb_done,
306	[NLMSG_OVERRUN]	= ynl_cb_noop,
307};
308
309/* Attribute validation */
310
311int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr)
312{
313	struct ynl_policy_attr *policy;
314	unsigned int type, len;
315	unsigned char *data;
316
317	data = mnl_attr_get_payload(attr);
318	len = mnl_attr_get_payload_len(attr);
319	type = mnl_attr_get_type(attr);
320	if (type > yarg->rsp_policy->max_attr) {
321		yerr(yarg->ys, YNL_ERROR_INTERNAL,
322		     "Internal error, validating unknown attribute");
323		return -1;
324	}
325
326	policy = &yarg->rsp_policy->table[type];
327
328	switch (policy->type) {
329	case YNL_PT_REJECT:
330		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
331		     "Rejected attribute (%s)", policy->name);
332		return -1;
333	case YNL_PT_IGNORE:
334		break;
335	case YNL_PT_U8:
336		if (len == sizeof(__u8))
337			break;
338		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
339		     "Invalid attribute (u8 %s)", policy->name);
340		return -1;
341	case YNL_PT_U16:
342		if (len == sizeof(__u16))
343			break;
344		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
345		     "Invalid attribute (u16 %s)", policy->name);
346		return -1;
347	case YNL_PT_U32:
348		if (len == sizeof(__u32))
349			break;
350		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
351		     "Invalid attribute (u32 %s)", policy->name);
352		return -1;
353	case YNL_PT_U64:
354		if (len == sizeof(__u64))
355			break;
356		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
357		     "Invalid attribute (u64 %s)", policy->name);
358		return -1;
359	case YNL_PT_UINT:
360		if (len == sizeof(__u32) || len == sizeof(__u64))
361			break;
362		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
363		     "Invalid attribute (uint %s)", policy->name);
364		return -1;
365	case YNL_PT_FLAG:
366		/* Let flags grow into real attrs, why not.. */
367		break;
368	case YNL_PT_NEST:
369		if (!len || len >= sizeof(*attr))
370			break;
371		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
372		     "Invalid attribute (nest %s)", policy->name);
373		return -1;
374	case YNL_PT_BINARY:
375		if (!policy->len || len == policy->len)
376			break;
377		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
378		     "Invalid attribute (binary %s)", policy->name);
379		return -1;
380	case YNL_PT_NUL_STR:
381		if ((!policy->len || len <= policy->len) && !data[len - 1])
382			break;
383		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
384		     "Invalid attribute (string %s)", policy->name);
385		return -1;
386	case YNL_PT_BITFIELD32:
387		if (len == sizeof(struct nla_bitfield32))
388			break;
389		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
390		     "Invalid attribute (bitfield32 %s)", policy->name);
391		return -1;
392	default:
393		yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
394		     "Invalid attribute (unknown %s)", policy->name);
395		return -1;
396	}
397
398	return 0;
399}
400
401/* Generic code */
402
403static void ynl_err_reset(struct ynl_sock *ys)
404{
405	ys->err.code = 0;
406	ys->err.attr_offs = 0;
407	ys->err.msg[0] = 0;
408}
409
410struct nlmsghdr *ynl_msg_start(struct ynl_sock *ys, __u32 id, __u16 flags)
411{
412	struct nlmsghdr *nlh;
413
414	ynl_err_reset(ys);
415
416	nlh = ys->nlh = mnl_nlmsg_put_header(ys->tx_buf);
417	nlh->nlmsg_type	= id;
418	nlh->nlmsg_flags = flags;
419	nlh->nlmsg_seq = ++ys->seq;
420
421	return nlh;
422}
423
424struct nlmsghdr *
425ynl_gemsg_start(struct ynl_sock *ys, __u32 id, __u16 flags,
426		__u8 cmd, __u8 version)
427{
428	struct genlmsghdr gehdr;
429	struct nlmsghdr *nlh;
430	void *data;
431
432	nlh = ynl_msg_start(ys, id, flags);
433
434	memset(&gehdr, 0, sizeof(gehdr));
435	gehdr.cmd = cmd;
436	gehdr.version = version;
437
438	data = mnl_nlmsg_put_extra_header(nlh, sizeof(gehdr));
439	memcpy(data, &gehdr, sizeof(gehdr));
440
441	return nlh;
442}
443
444void ynl_msg_start_req(struct ynl_sock *ys, __u32 id)
445{
446	ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK);
447}
448
449void ynl_msg_start_dump(struct ynl_sock *ys, __u32 id)
450{
451	ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
452}
453
454struct nlmsghdr *
455ynl_gemsg_start_req(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version)
456{
457	return ynl_gemsg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK, cmd, version);
458}
459
460struct nlmsghdr *
461ynl_gemsg_start_dump(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version)
462{
463	return ynl_gemsg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP,
464			       cmd, version);
465}
466
467int ynl_recv_ack(struct ynl_sock *ys, int ret)
468{
469	struct ynl_parse_arg yarg = { .ys = ys, };
470
471	if (!ret) {
472		yerr(ys, YNL_ERROR_EXPECT_ACK,
473		     "Expecting an ACK but nothing received");
474		return -1;
475	}
476
477	ret = mnl_socket_recvfrom(ys->sock, ys->rx_buf, MNL_SOCKET_BUFFER_SIZE);
478	if (ret < 0) {
479		perr(ys, "Socket receive failed");
480		return ret;
481	}
482	return mnl_cb_run(ys->rx_buf, ret, ys->seq, ys->portid,
483			  ynl_cb_null, &yarg);
484}
485
486int ynl_cb_null(const struct nlmsghdr *nlh, void *data)
487{
488	struct ynl_parse_arg *yarg = data;
489
490	yerr(yarg->ys, YNL_ERROR_UNEXPECT_MSG,
491	     "Received a message when none were expected");
492
493	return MNL_CB_ERROR;
494}
495
496/* Init/fini and genetlink boiler plate */
497static int
498ynl_get_family_info_mcast(struct ynl_sock *ys, const struct nlattr *mcasts)
499{
500	const struct nlattr *entry, *attr;
501	unsigned int i;
502
503	mnl_attr_for_each_nested(attr, mcasts)
504		ys->n_mcast_groups++;
505
506	if (!ys->n_mcast_groups)
507		return 0;
508
509	ys->mcast_groups = calloc(ys->n_mcast_groups,
510				  sizeof(*ys->mcast_groups));
511	if (!ys->mcast_groups)
512		return MNL_CB_ERROR;
513
514	i = 0;
515	mnl_attr_for_each_nested(entry, mcasts) {
516		mnl_attr_for_each_nested(attr, entry) {
517			if (mnl_attr_get_type(attr) == CTRL_ATTR_MCAST_GRP_ID)
518				ys->mcast_groups[i].id = mnl_attr_get_u32(attr);
519			if (mnl_attr_get_type(attr) == CTRL_ATTR_MCAST_GRP_NAME) {
520				strncpy(ys->mcast_groups[i].name,
521					mnl_attr_get_str(attr),
522					GENL_NAMSIZ - 1);
523				ys->mcast_groups[i].name[GENL_NAMSIZ - 1] = 0;
524			}
525		}
526		i++;
527	}
528
529	return 0;
530}
531
532static int ynl_get_family_info_cb(const struct nlmsghdr *nlh, void *data)
533{
534	struct ynl_parse_arg *yarg = data;
535	struct ynl_sock *ys = yarg->ys;
536	const struct nlattr *attr;
537	bool found_id = true;
538
539	mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) {
540		if (mnl_attr_get_type(attr) == CTRL_ATTR_MCAST_GROUPS)
541			if (ynl_get_family_info_mcast(ys, attr))
542				return MNL_CB_ERROR;
543
544		if (mnl_attr_get_type(attr) != CTRL_ATTR_FAMILY_ID)
545			continue;
546
547		if (mnl_attr_get_payload_len(attr) != sizeof(__u16)) {
548			yerr(ys, YNL_ERROR_ATTR_INVALID, "Invalid family ID");
549			return MNL_CB_ERROR;
550		}
551
552		ys->family_id = mnl_attr_get_u16(attr);
553		found_id = true;
554	}
555
556	if (!found_id) {
557		yerr(ys, YNL_ERROR_ATTR_MISSING, "Family ID missing");
558		return MNL_CB_ERROR;
559	}
560	return MNL_CB_OK;
561}
562
563static int ynl_sock_read_family(struct ynl_sock *ys, const char *family_name)
564{
565	struct ynl_parse_arg yarg = { .ys = ys, };
566	struct nlmsghdr *nlh;
567	int err;
568
569	nlh = ynl_gemsg_start_req(ys, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, 1);
570	mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name);
571
572	err = mnl_socket_sendto(ys->sock, nlh, nlh->nlmsg_len);
573	if (err < 0) {
574		perr(ys, "failed to request socket family info");
575		return err;
576	}
577
578	err = mnl_socket_recvfrom(ys->sock, ys->rx_buf, MNL_SOCKET_BUFFER_SIZE);
579	if (err <= 0) {
580		perr(ys, "failed to receive the socket family info");
581		return err;
582	}
583	err = mnl_cb_run2(ys->rx_buf, err, ys->seq, ys->portid,
584			  ynl_get_family_info_cb, &yarg,
585			  ynl_cb_array, ARRAY_SIZE(ynl_cb_array));
586	if (err < 0) {
587		free(ys->mcast_groups);
588		perr(ys, "failed to receive the socket family info - no such family?");
589		return err;
590	}
591
592	err = ynl_recv_ack(ys, err);
593	if (err < 0) {
594		free(ys->mcast_groups);
595		return err;
596	}
597
598	return 0;
599}
600
601struct ynl_sock *
602ynl_sock_create(const struct ynl_family *yf, struct ynl_error *yse)
603{
604	struct ynl_sock *ys;
605	int one = 1;
606
607	ys = malloc(sizeof(*ys) + 2 * MNL_SOCKET_BUFFER_SIZE);
608	if (!ys)
609		return NULL;
610	memset(ys, 0, sizeof(*ys));
611
612	ys->family = yf;
613	ys->tx_buf = &ys->raw_buf[0];
614	ys->rx_buf = &ys->raw_buf[MNL_SOCKET_BUFFER_SIZE];
615	ys->ntf_last_next = &ys->ntf_first;
616
617	ys->sock = mnl_socket_open(NETLINK_GENERIC);
618	if (!ys->sock) {
619		__perr(yse, "failed to create a netlink socket");
620		goto err_free_sock;
621	}
622
623	if (mnl_socket_setsockopt(ys->sock, NETLINK_CAP_ACK,
624				  &one, sizeof(one))) {
625		__perr(yse, "failed to enable netlink ACK");
626		goto err_close_sock;
627	}
628	if (mnl_socket_setsockopt(ys->sock, NETLINK_EXT_ACK,
629				  &one, sizeof(one))) {
630		__perr(yse, "failed to enable netlink ext ACK");
631		goto err_close_sock;
632	}
633
634	ys->seq = random();
635	ys->portid = mnl_socket_get_portid(ys->sock);
636
637	if (ynl_sock_read_family(ys, yf->name)) {
638		if (yse)
639			memcpy(yse, &ys->err, sizeof(*yse));
640		goto err_close_sock;
641	}
642
643	return ys;
644
645err_close_sock:
646	mnl_socket_close(ys->sock);
647err_free_sock:
648	free(ys);
649	return NULL;
650}
651
652void ynl_sock_destroy(struct ynl_sock *ys)
653{
654	struct ynl_ntf_base_type *ntf;
655
656	mnl_socket_close(ys->sock);
657	while ((ntf = ynl_ntf_dequeue(ys)))
658		ynl_ntf_free(ntf);
659	free(ys->mcast_groups);
660	free(ys);
661}
662
663/* YNL multicast handling */
664
665void ynl_ntf_free(struct ynl_ntf_base_type *ntf)
666{
667	ntf->free(ntf);
668}
669
670int ynl_subscribe(struct ynl_sock *ys, const char *grp_name)
671{
672	unsigned int i;
673	int err;
674
675	for (i = 0; i < ys->n_mcast_groups; i++)
676		if (!strcmp(ys->mcast_groups[i].name, grp_name))
677			break;
678	if (i == ys->n_mcast_groups) {
679		yerr(ys, ENOENT, "Multicast group '%s' not found", grp_name);
680		return -1;
681	}
682
683	err = mnl_socket_setsockopt(ys->sock, NETLINK_ADD_MEMBERSHIP,
684				    &ys->mcast_groups[i].id,
685				    sizeof(ys->mcast_groups[i].id));
686	if (err < 0) {
687		perr(ys, "Subscribing to multicast group failed");
688		return -1;
689	}
690
691	return 0;
692}
693
694int ynl_socket_get_fd(struct ynl_sock *ys)
695{
696	return mnl_socket_get_fd(ys->sock);
697}
698
699struct ynl_ntf_base_type *ynl_ntf_dequeue(struct ynl_sock *ys)
700{
701	struct ynl_ntf_base_type *ntf;
702
703	if (!ynl_has_ntf(ys))
704		return NULL;
705
706	ntf = ys->ntf_first;
707	ys->ntf_first = ntf->next;
708	if (ys->ntf_last_next == &ntf->next)
709		ys->ntf_last_next = &ys->ntf_first;
710
711	return ntf;
712}
713
714static int ynl_ntf_parse(struct ynl_sock *ys, const struct nlmsghdr *nlh)
715{
716	struct ynl_parse_arg yarg = { .ys = ys, };
717	const struct ynl_ntf_info *info;
718	struct ynl_ntf_base_type *rsp;
719	struct genlmsghdr *gehdr;
720	int ret;
721
722	gehdr = mnl_nlmsg_get_payload(nlh);
723	if (gehdr->cmd >= ys->family->ntf_info_size)
724		return MNL_CB_ERROR;
725	info = &ys->family->ntf_info[gehdr->cmd];
726	if (!info->cb)
727		return MNL_CB_ERROR;
728
729	rsp = calloc(1, info->alloc_sz);
730	rsp->free = info->free;
731	yarg.data = rsp->data;
732	yarg.rsp_policy = info->policy;
733
734	ret = info->cb(nlh, &yarg);
735	if (ret <= MNL_CB_STOP)
736		goto err_free;
737
738	rsp->family = nlh->nlmsg_type;
739	rsp->cmd = gehdr->cmd;
740
741	*ys->ntf_last_next = rsp;
742	ys->ntf_last_next = &rsp->next;
743
744	return MNL_CB_OK;
745
746err_free:
747	info->free(rsp);
748	return MNL_CB_ERROR;
749}
750
751static int ynl_ntf_trampoline(const struct nlmsghdr *nlh, void *data)
752{
753	struct ynl_parse_arg *yarg = data;
754
755	return ynl_ntf_parse(yarg->ys, nlh);
756}
757
758int ynl_ntf_check(struct ynl_sock *ys)
759{
760	struct ynl_parse_arg yarg = { .ys = ys, };
761	ssize_t len;
762	int err;
763
764	do {
765		/* libmnl doesn't let us pass flags to the recv to make
766		 * it non-blocking so we need to poll() or peek() :|
767		 */
768		struct pollfd pfd = { };
769
770		pfd.fd = mnl_socket_get_fd(ys->sock);
771		pfd.events = POLLIN;
772		err = poll(&pfd, 1, 1);
773		if (err < 1)
774			return err;
775
776		len = mnl_socket_recvfrom(ys->sock, ys->rx_buf,
777					  MNL_SOCKET_BUFFER_SIZE);
778		if (len < 0)
779			return len;
780
781		err = mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid,
782				  ynl_ntf_trampoline, &yarg,
783				  ynl_cb_array, NLMSG_MIN_TYPE);
784		if (err < 0)
785			return err;
786	} while (err > 0);
787
788	return 0;
789}
790
791/* YNL specific helpers used by the auto-generated code */
792
793struct ynl_dump_list_type *YNL_LIST_END = (void *)(0xb4d123);
794
795void ynl_error_unknown_notification(struct ynl_sock *ys, __u8 cmd)
796{
797	yerr(ys, YNL_ERROR_UNKNOWN_NTF,
798	     "Unknown notification message type '%d'", cmd);
799}
800
801int ynl_error_parse(struct ynl_parse_arg *yarg, const char *msg)
802{
803	yerr(yarg->ys, YNL_ERROR_INV_RESP, "Error parsing response: %s", msg);
804	return MNL_CB_ERROR;
805}
806
807static int
808ynl_check_alien(struct ynl_sock *ys, const struct nlmsghdr *nlh, __u32 rsp_cmd)
809{
810	struct genlmsghdr *gehdr;
811
812	if (mnl_nlmsg_get_payload_len(nlh) < sizeof(*gehdr)) {
813		yerr(ys, YNL_ERROR_INV_RESP,
814		     "Kernel responded with truncated message");
815		return -1;
816	}
817
818	gehdr = mnl_nlmsg_get_payload(nlh);
819	if (gehdr->cmd != rsp_cmd)
820		return ynl_ntf_parse(ys, nlh);
821
822	return 0;
823}
824
825static int ynl_req_trampoline(const struct nlmsghdr *nlh, void *data)
826{
827	struct ynl_req_state *yrs = data;
828	int ret;
829
830	ret = ynl_check_alien(yrs->yarg.ys, nlh, yrs->rsp_cmd);
831	if (ret)
832		return ret < 0 ? MNL_CB_ERROR : MNL_CB_OK;
833
834	return yrs->cb(nlh, &yrs->yarg);
835}
836
837int ynl_exec(struct ynl_sock *ys, struct nlmsghdr *req_nlh,
838	     struct ynl_req_state *yrs)
839{
840	ssize_t len;
841	int err;
842
843	err = mnl_socket_sendto(ys->sock, req_nlh, req_nlh->nlmsg_len);
844	if (err < 0)
845		return err;
846
847	do {
848		len = mnl_socket_recvfrom(ys->sock, ys->rx_buf,
849					  MNL_SOCKET_BUFFER_SIZE);
850		if (len < 0)
851			return len;
852
853		err = mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid,
854				  ynl_req_trampoline, yrs,
855				  ynl_cb_array, NLMSG_MIN_TYPE);
856		if (err < 0)
857			return err;
858	} while (err > 0);
859
860	return 0;
861}
862
863static int ynl_dump_trampoline(const struct nlmsghdr *nlh, void *data)
864{
865	struct ynl_dump_state *ds = data;
866	struct ynl_dump_list_type *obj;
867	struct ynl_parse_arg yarg = {};
868	int ret;
869
870	ret = ynl_check_alien(ds->ys, nlh, ds->rsp_cmd);
871	if (ret)
872		return ret < 0 ? MNL_CB_ERROR : MNL_CB_OK;
873
874	obj = calloc(1, ds->alloc_sz);
875	if (!obj)
876		return MNL_CB_ERROR;
877
878	if (!ds->first)
879		ds->first = obj;
880	if (ds->last)
881		ds->last->next = obj;
882	ds->last = obj;
883
884	yarg.ys = ds->ys;
885	yarg.rsp_policy = ds->rsp_policy;
886	yarg.data = &obj->data;
887
888	return ds->cb(nlh, &yarg);
889}
890
891static void *ynl_dump_end(struct ynl_dump_state *ds)
892{
893	if (!ds->first)
894		return YNL_LIST_END;
895
896	ds->last->next = YNL_LIST_END;
897	return ds->first;
898}
899
900int ynl_exec_dump(struct ynl_sock *ys, struct nlmsghdr *req_nlh,
901		  struct ynl_dump_state *yds)
902{
903	ssize_t len;
904	int err;
905
906	err = mnl_socket_sendto(ys->sock, req_nlh, req_nlh->nlmsg_len);
907	if (err < 0)
908		return err;
909
910	do {
911		len = mnl_socket_recvfrom(ys->sock, ys->rx_buf,
912					  MNL_SOCKET_BUFFER_SIZE);
913		if (len < 0)
914			goto err_close_list;
915
916		err = mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid,
917				  ynl_dump_trampoline, yds,
918				  ynl_cb_array, NLMSG_MIN_TYPE);
919		if (err < 0)
920			goto err_close_list;
921	} while (err > 0);
922
923	yds->first = ynl_dump_end(yds);
924	return 0;
925
926err_close_list:
927	yds->first = ynl_dump_end(yds);
928	return -1;
929}