brintos

brintos / linux-shallow public Read only

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