xref: /aosp_15_r20/external/ethtool/netlink/netlink.c (revision 1b481fc3bb1b45d4cf28d1ec12969dc1055f555d)
1*1b481fc3SMaciej Żenczykowski /*
2*1b481fc3SMaciej Żenczykowski  * netlink.c - basic infrastructure for netlink code
3*1b481fc3SMaciej Żenczykowski  *
4*1b481fc3SMaciej Żenczykowski  * Heart of the netlink interface implementation.
5*1b481fc3SMaciej Żenczykowski  */
6*1b481fc3SMaciej Żenczykowski 
7*1b481fc3SMaciej Żenczykowski #include <errno.h>
8*1b481fc3SMaciej Żenczykowski 
9*1b481fc3SMaciej Żenczykowski #include "../internal.h"
10*1b481fc3SMaciej Żenczykowski #include "netlink.h"
11*1b481fc3SMaciej Żenczykowski #include "extapi.h"
12*1b481fc3SMaciej Żenczykowski #include "msgbuff.h"
13*1b481fc3SMaciej Żenczykowski #include "nlsock.h"
14*1b481fc3SMaciej Żenczykowski #include "strset.h"
15*1b481fc3SMaciej Żenczykowski 
16*1b481fc3SMaciej Żenczykowski /* Used as reply callback for requests where no reply is expected (e.g. most
17*1b481fc3SMaciej Żenczykowski  * "set" type commands)
18*1b481fc3SMaciej Żenczykowski  */
nomsg_reply_cb(const struct nlmsghdr * nlhdr,void * data __maybe_unused)19*1b481fc3SMaciej Żenczykowski int nomsg_reply_cb(const struct nlmsghdr *nlhdr, void *data __maybe_unused)
20*1b481fc3SMaciej Żenczykowski {
21*1b481fc3SMaciej Żenczykowski 	const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
22*1b481fc3SMaciej Żenczykowski 
23*1b481fc3SMaciej Żenczykowski 	fprintf(stderr, "received unexpected message: len=%u type=%u cmd=%u\n",
24*1b481fc3SMaciej Żenczykowski 		nlhdr->nlmsg_len, nlhdr->nlmsg_type, ghdr->cmd);
25*1b481fc3SMaciej Żenczykowski 	return MNL_CB_OK;
26*1b481fc3SMaciej Żenczykowski }
27*1b481fc3SMaciej Żenczykowski 
28*1b481fc3SMaciej Żenczykowski /* standard attribute parser callback; it fills provided array with pointers
29*1b481fc3SMaciej Żenczykowski  * to attributes like kernel nla_parse(). We must expect to run on top of
30*1b481fc3SMaciej Żenczykowski  * a newer kernel which may send attributes that we do not know (yet). Rather
31*1b481fc3SMaciej Żenczykowski  * than treating them as an error, just ignore them.
32*1b481fc3SMaciej Żenczykowski  */
attr_cb(const struct nlattr * attr,void * data)33*1b481fc3SMaciej Żenczykowski int attr_cb(const struct nlattr *attr, void *data)
34*1b481fc3SMaciej Żenczykowski {
35*1b481fc3SMaciej Żenczykowski 	const struct attr_tb_info *tb_info = data;
36*1b481fc3SMaciej Żenczykowski 	uint16_t type = mnl_attr_get_type(attr);
37*1b481fc3SMaciej Żenczykowski 
38*1b481fc3SMaciej Żenczykowski 	if (type <= tb_info->max_type)
39*1b481fc3SMaciej Żenczykowski 		tb_info->tb[type] = attr;
40*1b481fc3SMaciej Żenczykowski 
41*1b481fc3SMaciej Żenczykowski 	return MNL_CB_OK;
42*1b481fc3SMaciej Żenczykowski }
43*1b481fc3SMaciej Żenczykowski 
44*1b481fc3SMaciej Żenczykowski /* misc helpers */
45*1b481fc3SMaciej Żenczykowski 
get_dev_name(const struct nlattr * nest)46*1b481fc3SMaciej Żenczykowski const char *get_dev_name(const struct nlattr *nest)
47*1b481fc3SMaciej Żenczykowski {
48*1b481fc3SMaciej Żenczykowski 	const struct nlattr *tb[ETHTOOL_A_HEADER_MAX + 1] = {};
49*1b481fc3SMaciej Żenczykowski 	DECLARE_ATTR_TB_INFO(tb);
50*1b481fc3SMaciej Żenczykowski 	int ret;
51*1b481fc3SMaciej Żenczykowski 
52*1b481fc3SMaciej Żenczykowski 	if (!nest)
53*1b481fc3SMaciej Żenczykowski 		return NULL;
54*1b481fc3SMaciej Żenczykowski 	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
55*1b481fc3SMaciej Żenczykowski 	if (ret < 0 || !tb[ETHTOOL_A_HEADER_DEV_NAME])
56*1b481fc3SMaciej Żenczykowski 		return "(none)";
57*1b481fc3SMaciej Żenczykowski 	return mnl_attr_get_str(tb[ETHTOOL_A_HEADER_DEV_NAME]);
58*1b481fc3SMaciej Żenczykowski }
59*1b481fc3SMaciej Żenczykowski 
get_dev_info(const struct nlattr * nest,int * ifindex,char * ifname)60*1b481fc3SMaciej Żenczykowski int get_dev_info(const struct nlattr *nest, int *ifindex, char *ifname)
61*1b481fc3SMaciej Żenczykowski {
62*1b481fc3SMaciej Żenczykowski 	const struct nlattr *tb[ETHTOOL_A_HEADER_MAX + 1] = {};
63*1b481fc3SMaciej Żenczykowski 	const struct nlattr *index_attr;
64*1b481fc3SMaciej Żenczykowski 	const struct nlattr *name_attr;
65*1b481fc3SMaciej Żenczykowski 	DECLARE_ATTR_TB_INFO(tb);
66*1b481fc3SMaciej Żenczykowski 	int ret;
67*1b481fc3SMaciej Żenczykowski 
68*1b481fc3SMaciej Żenczykowski 	if (ifindex)
69*1b481fc3SMaciej Żenczykowski 		*ifindex = 0;
70*1b481fc3SMaciej Żenczykowski 	if (ifname)
71*1b481fc3SMaciej Żenczykowski 		memset(ifname, '\0', ALTIFNAMSIZ);
72*1b481fc3SMaciej Żenczykowski 
73*1b481fc3SMaciej Żenczykowski 	if (!nest)
74*1b481fc3SMaciej Żenczykowski 		return -EFAULT;
75*1b481fc3SMaciej Żenczykowski 	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
76*1b481fc3SMaciej Żenczykowski 	index_attr = tb[ETHTOOL_A_HEADER_DEV_INDEX];
77*1b481fc3SMaciej Żenczykowski 	name_attr = tb[ETHTOOL_A_HEADER_DEV_NAME];
78*1b481fc3SMaciej Żenczykowski 	if (ret < 0 || (ifindex && !index_attr) || (ifname && !name_attr))
79*1b481fc3SMaciej Żenczykowski 		return -EFAULT;
80*1b481fc3SMaciej Żenczykowski 
81*1b481fc3SMaciej Żenczykowski 	if (ifindex)
82*1b481fc3SMaciej Żenczykowski 		*ifindex = mnl_attr_get_u32(index_attr);
83*1b481fc3SMaciej Żenczykowski 	if (ifname) {
84*1b481fc3SMaciej Żenczykowski 		strncpy(ifname, mnl_attr_get_str(name_attr), ALTIFNAMSIZ);
85*1b481fc3SMaciej Żenczykowski 		if (ifname[ALTIFNAMSIZ - 1]) {
86*1b481fc3SMaciej Żenczykowski 			ifname[ALTIFNAMSIZ - 1] = '\0';
87*1b481fc3SMaciej Żenczykowski 			fprintf(stderr, "kernel device name too long: '%s'\n",
88*1b481fc3SMaciej Żenczykowski 				mnl_attr_get_str(name_attr));
89*1b481fc3SMaciej Żenczykowski 			return -EFAULT;
90*1b481fc3SMaciej Żenczykowski 		}
91*1b481fc3SMaciej Żenczykowski 	}
92*1b481fc3SMaciej Żenczykowski 	return 0;
93*1b481fc3SMaciej Żenczykowski }
94*1b481fc3SMaciej Żenczykowski 
95*1b481fc3SMaciej Żenczykowski /**
96*1b481fc3SMaciej Żenczykowski  * netlink_cmd_check() - check support for netlink command
97*1b481fc3SMaciej Żenczykowski  * @ctx:            ethtool command context
98*1b481fc3SMaciej Żenczykowski  * @cmd:            netlink command id
99*1b481fc3SMaciej Żenczykowski  * @devname:        device name from user
100*1b481fc3SMaciej Żenczykowski  * @allow_wildcard: wildcard dumps supported
101*1b481fc3SMaciej Żenczykowski  *
102*1b481fc3SMaciej Żenczykowski  * Check if command @cmd is known to be unsupported based on ops information
103*1b481fc3SMaciej Żenczykowski  * from genetlink family id request. Set nlctx->ioctl_fallback if ethtool
104*1b481fc3SMaciej Żenczykowski  * should fall back to ioctl, i.e. when we do not know in advance that
105*1b481fc3SMaciej Żenczykowski  * netlink request is supported. Set nlctx->wildcard_unsupported if "*" was
106*1b481fc3SMaciej Żenczykowski  * used as device name but the request does not support wildcards (on either
107*1b481fc3SMaciej Żenczykowski  * side).
108*1b481fc3SMaciej Żenczykowski  *
109*1b481fc3SMaciej Żenczykowski  * Return: true if we know the netlink request is not supported and should
110*1b481fc3SMaciej Żenczykowski  * fail (and possibly fall back) without actually sending it to kernel.
111*1b481fc3SMaciej Żenczykowski  */
netlink_cmd_check(struct cmd_context * ctx,unsigned int cmd,bool allow_wildcard)112*1b481fc3SMaciej Żenczykowski bool netlink_cmd_check(struct cmd_context *ctx, unsigned int cmd,
113*1b481fc3SMaciej Żenczykowski 		       bool allow_wildcard)
114*1b481fc3SMaciej Żenczykowski {
115*1b481fc3SMaciej Żenczykowski 	bool is_dump = !strcmp(ctx->devname, WILDCARD_DEVNAME);
116*1b481fc3SMaciej Żenczykowski 	uint32_t cap = is_dump ? GENL_CMD_CAP_DUMP : GENL_CMD_CAP_DO;
117*1b481fc3SMaciej Żenczykowski 	struct nl_context *nlctx = ctx->nlctx;
118*1b481fc3SMaciej Żenczykowski 
119*1b481fc3SMaciej Żenczykowski 	if (is_dump && !allow_wildcard) {
120*1b481fc3SMaciej Żenczykowski 		nlctx->wildcard_unsupported = true;
121*1b481fc3SMaciej Żenczykowski 		return true;
122*1b481fc3SMaciej Żenczykowski 	}
123*1b481fc3SMaciej Żenczykowski 	if (!nlctx->ops_info) {
124*1b481fc3SMaciej Żenczykowski 		nlctx->ioctl_fallback = true;
125*1b481fc3SMaciej Żenczykowski 		return false;
126*1b481fc3SMaciej Żenczykowski 	}
127*1b481fc3SMaciej Żenczykowski 	if (cmd > ETHTOOL_MSG_USER_MAX || !nlctx->ops_info[cmd].op_flags) {
128*1b481fc3SMaciej Żenczykowski 		nlctx->ioctl_fallback = true;
129*1b481fc3SMaciej Żenczykowski 		return true;
130*1b481fc3SMaciej Żenczykowski 	}
131*1b481fc3SMaciej Żenczykowski 
132*1b481fc3SMaciej Żenczykowski 	if (is_dump && !(nlctx->ops_info[cmd].op_flags & GENL_CMD_CAP_DUMP))
133*1b481fc3SMaciej Żenczykowski 		nlctx->wildcard_unsupported = true;
134*1b481fc3SMaciej Żenczykowski 
135*1b481fc3SMaciej Żenczykowski 	return !(nlctx->ops_info[cmd].op_flags & cap);
136*1b481fc3SMaciej Żenczykowski }
137*1b481fc3SMaciej Żenczykowski 
138*1b481fc3SMaciej Żenczykowski struct ethtool_op_policy_query_ctx {
139*1b481fc3SMaciej Żenczykowski 	struct nl_context *nlctx;
140*1b481fc3SMaciej Żenczykowski 	unsigned int op;
141*1b481fc3SMaciej Żenczykowski 	unsigned int op_hdr_attr;
142*1b481fc3SMaciej Żenczykowski 
143*1b481fc3SMaciej Żenczykowski 	bool op_policy_found;
144*1b481fc3SMaciej Żenczykowski 	bool hdr_policy_found;
145*1b481fc3SMaciej Żenczykowski 	unsigned int op_policy_idx;
146*1b481fc3SMaciej Żenczykowski 	unsigned int hdr_policy_idx;
147*1b481fc3SMaciej Żenczykowski 	uint64_t flag_mask;
148*1b481fc3SMaciej Żenczykowski };
149*1b481fc3SMaciej Żenczykowski 
family_policy_find_op(struct ethtool_op_policy_query_ctx * policy_ctx,const struct nlattr * op_policy)150*1b481fc3SMaciej Żenczykowski static int family_policy_find_op(struct ethtool_op_policy_query_ctx *policy_ctx,
151*1b481fc3SMaciej Żenczykowski 				 const struct nlattr *op_policy)
152*1b481fc3SMaciej Żenczykowski {
153*1b481fc3SMaciej Żenczykowski 	const struct nlattr *attr;
154*1b481fc3SMaciej Żenczykowski 	unsigned int type;
155*1b481fc3SMaciej Żenczykowski 	int ret;
156*1b481fc3SMaciej Żenczykowski 
157*1b481fc3SMaciej Żenczykowski 	type = policy_ctx->nlctx->is_dump ?
158*1b481fc3SMaciej Żenczykowski 		CTRL_ATTR_POLICY_DUMP : CTRL_ATTR_POLICY_DO;
159*1b481fc3SMaciej Żenczykowski 
160*1b481fc3SMaciej Żenczykowski 	mnl_attr_for_each_nested(attr, op_policy) {
161*1b481fc3SMaciej Żenczykowski 		const struct nlattr *tb[CTRL_ATTR_POLICY_DUMP_MAX + 1] = {};
162*1b481fc3SMaciej Żenczykowski 		DECLARE_ATTR_TB_INFO(tb);
163*1b481fc3SMaciej Żenczykowski 
164*1b481fc3SMaciej Żenczykowski 		if (mnl_attr_get_type(attr) != policy_ctx->op)
165*1b481fc3SMaciej Żenczykowski 			continue;
166*1b481fc3SMaciej Żenczykowski 
167*1b481fc3SMaciej Żenczykowski 		ret = mnl_attr_parse_nested(attr, attr_cb, &tb_info);
168*1b481fc3SMaciej Żenczykowski 		if (ret < 0)
169*1b481fc3SMaciej Żenczykowski 			return ret;
170*1b481fc3SMaciej Żenczykowski 
171*1b481fc3SMaciej Żenczykowski 		if (!tb[type])
172*1b481fc3SMaciej Żenczykowski 			continue;
173*1b481fc3SMaciej Żenczykowski 
174*1b481fc3SMaciej Żenczykowski 		policy_ctx->op_policy_found = true;
175*1b481fc3SMaciej Żenczykowski 		policy_ctx->op_policy_idx = mnl_attr_get_u32(tb[type]);
176*1b481fc3SMaciej Żenczykowski 		break;
177*1b481fc3SMaciej Żenczykowski 	}
178*1b481fc3SMaciej Żenczykowski 
179*1b481fc3SMaciej Żenczykowski 	return 0;
180*1b481fc3SMaciej Żenczykowski }
181*1b481fc3SMaciej Żenczykowski 
family_policy_cb(const struct nlmsghdr * nlhdr,void * data)182*1b481fc3SMaciej Żenczykowski static int family_policy_cb(const struct nlmsghdr *nlhdr, void *data)
183*1b481fc3SMaciej Żenczykowski {
184*1b481fc3SMaciej Żenczykowski 	const struct nlattr *tba[NL_POLICY_TYPE_ATTR_MAX + 1] = {};
185*1b481fc3SMaciej Żenczykowski 	DECLARE_ATTR_TB_INFO(tba);
186*1b481fc3SMaciej Żenczykowski 	const struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
187*1b481fc3SMaciej Żenczykowski 	DECLARE_ATTR_TB_INFO(tb);
188*1b481fc3SMaciej Żenczykowski 	struct ethtool_op_policy_query_ctx *policy_ctx = data;
189*1b481fc3SMaciej Żenczykowski 	const struct nlattr *policy_attr, *attr_attr, *attr;
190*1b481fc3SMaciej Żenczykowski 	unsigned int attr_idx, policy_idx;
191*1b481fc3SMaciej Żenczykowski 	int ret;
192*1b481fc3SMaciej Żenczykowski 
193*1b481fc3SMaciej Żenczykowski 	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
194*1b481fc3SMaciej Żenczykowski 	if (ret < 0)
195*1b481fc3SMaciej Żenczykowski 		return MNL_CB_ERROR;
196*1b481fc3SMaciej Żenczykowski 
197*1b481fc3SMaciej Żenczykowski 	if (!policy_ctx->op_policy_found) {
198*1b481fc3SMaciej Żenczykowski 		if (!tb[CTRL_ATTR_OP_POLICY]) {
199*1b481fc3SMaciej Żenczykowski 			fprintf(stderr, "Error: op policy map not present\n");
200*1b481fc3SMaciej Żenczykowski 			return MNL_CB_ERROR;
201*1b481fc3SMaciej Żenczykowski 		}
202*1b481fc3SMaciej Żenczykowski 		ret = family_policy_find_op(policy_ctx, tb[CTRL_ATTR_OP_POLICY]);
203*1b481fc3SMaciej Żenczykowski 		return ret < 0 ? MNL_CB_ERROR : MNL_CB_OK;
204*1b481fc3SMaciej Żenczykowski 	}
205*1b481fc3SMaciej Żenczykowski 
206*1b481fc3SMaciej Żenczykowski 	if (!tb[CTRL_ATTR_POLICY])
207*1b481fc3SMaciej Żenczykowski 		return MNL_CB_OK;
208*1b481fc3SMaciej Żenczykowski 
209*1b481fc3SMaciej Żenczykowski 	policy_attr = mnl_attr_get_payload(tb[CTRL_ATTR_POLICY]);
210*1b481fc3SMaciej Żenczykowski 	policy_idx = mnl_attr_get_type(policy_attr);
211*1b481fc3SMaciej Żenczykowski 	attr_attr = mnl_attr_get_payload(policy_attr);
212*1b481fc3SMaciej Żenczykowski 	attr_idx = mnl_attr_get_type(attr_attr);
213*1b481fc3SMaciej Żenczykowski 
214*1b481fc3SMaciej Żenczykowski 	ret = mnl_attr_parse_nested(attr_attr, attr_cb, &tba_info);
215*1b481fc3SMaciej Żenczykowski 	if (ret < 0)
216*1b481fc3SMaciej Żenczykowski 		return MNL_CB_ERROR;
217*1b481fc3SMaciej Żenczykowski 
218*1b481fc3SMaciej Żenczykowski 	if (policy_idx == policy_ctx->op_policy_idx &&
219*1b481fc3SMaciej Żenczykowski 	    attr_idx == policy_ctx->op_hdr_attr) {
220*1b481fc3SMaciej Żenczykowski 		attr = tba[NL_POLICY_TYPE_ATTR_POLICY_IDX];
221*1b481fc3SMaciej Żenczykowski 		if (!attr) {
222*1b481fc3SMaciej Żenczykowski 			fprintf(stderr,	"Error: no policy index in what was expected to be ethtool header attribute\n");
223*1b481fc3SMaciej Żenczykowski 			return MNL_CB_ERROR;
224*1b481fc3SMaciej Żenczykowski 		}
225*1b481fc3SMaciej Żenczykowski 		policy_ctx->hdr_policy_found = true;
226*1b481fc3SMaciej Żenczykowski 		policy_ctx->hdr_policy_idx = mnl_attr_get_u32(attr);
227*1b481fc3SMaciej Żenczykowski 	}
228*1b481fc3SMaciej Żenczykowski 
229*1b481fc3SMaciej Żenczykowski 	if (policy_ctx->hdr_policy_found &&
230*1b481fc3SMaciej Żenczykowski 	    policy_ctx->hdr_policy_idx == policy_idx &&
231*1b481fc3SMaciej Żenczykowski 	    attr_idx == ETHTOOL_A_HEADER_FLAGS) {
232*1b481fc3SMaciej Żenczykowski 		attr = tba[NL_POLICY_TYPE_ATTR_MASK];
233*1b481fc3SMaciej Żenczykowski 		if (!attr) {
234*1b481fc3SMaciej Żenczykowski 			fprintf(stderr,	"Error: validation mask not reported for ethtool header flags\n");
235*1b481fc3SMaciej Żenczykowski 			return MNL_CB_ERROR;
236*1b481fc3SMaciej Żenczykowski 		}
237*1b481fc3SMaciej Żenczykowski 
238*1b481fc3SMaciej Żenczykowski 		policy_ctx->flag_mask = mnl_attr_get_u64(attr);
239*1b481fc3SMaciej Żenczykowski 	}
240*1b481fc3SMaciej Żenczykowski 
241*1b481fc3SMaciej Żenczykowski 	return MNL_CB_OK;
242*1b481fc3SMaciej Żenczykowski }
243*1b481fc3SMaciej Żenczykowski 
read_flags_policy(struct nl_context * nlctx,struct nl_socket * nlsk,unsigned int nlcmd,unsigned int hdrattr)244*1b481fc3SMaciej Żenczykowski static int read_flags_policy(struct nl_context *nlctx, struct nl_socket *nlsk,
245*1b481fc3SMaciej Żenczykowski 			     unsigned int nlcmd, unsigned int hdrattr)
246*1b481fc3SMaciej Żenczykowski {
247*1b481fc3SMaciej Żenczykowski 	struct ethtool_op_policy_query_ctx policy_ctx;
248*1b481fc3SMaciej Żenczykowski 	struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
249*1b481fc3SMaciej Żenczykowski 	int ret;
250*1b481fc3SMaciej Żenczykowski 
251*1b481fc3SMaciej Żenczykowski 	if (nlctx->ops_info[nlcmd].hdr_policy_loaded)
252*1b481fc3SMaciej Żenczykowski 		return 0;
253*1b481fc3SMaciej Żenczykowski 
254*1b481fc3SMaciej Żenczykowski 	memset(&policy_ctx, 0, sizeof(policy_ctx));
255*1b481fc3SMaciej Żenczykowski 	policy_ctx.nlctx = nlctx;
256*1b481fc3SMaciej Żenczykowski 	policy_ctx.op = nlcmd;
257*1b481fc3SMaciej Żenczykowski 	policy_ctx.op_hdr_attr = hdrattr;
258*1b481fc3SMaciej Żenczykowski 
259*1b481fc3SMaciej Żenczykowski 	ret = __msg_init(msgbuff, GENL_ID_CTRL, CTRL_CMD_GETPOLICY,
260*1b481fc3SMaciej Żenczykowski 			 NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP, 1);
261*1b481fc3SMaciej Żenczykowski 	if (ret < 0)
262*1b481fc3SMaciej Żenczykowski 		return ret;
263*1b481fc3SMaciej Żenczykowski 	ret = -EMSGSIZE;
264*1b481fc3SMaciej Żenczykowski 	if (ethnla_put_u16(msgbuff, CTRL_ATTR_FAMILY_ID, nlctx->ethnl_fam))
265*1b481fc3SMaciej Żenczykowski 		return ret;
266*1b481fc3SMaciej Żenczykowski 	if (ethnla_put_u32(msgbuff, CTRL_ATTR_OP, nlcmd))
267*1b481fc3SMaciej Żenczykowski 		return ret;
268*1b481fc3SMaciej Żenczykowski 
269*1b481fc3SMaciej Żenczykowski 	nlsock_sendmsg(nlsk, NULL);
270*1b481fc3SMaciej Żenczykowski 	nlsock_process_reply(nlsk, family_policy_cb, &policy_ctx);
271*1b481fc3SMaciej Żenczykowski 
272*1b481fc3SMaciej Żenczykowski 	nlctx->ops_info[nlcmd].hdr_policy_loaded = 1;
273*1b481fc3SMaciej Żenczykowski 	nlctx->ops_info[nlcmd].hdr_flags = policy_ctx.flag_mask;
274*1b481fc3SMaciej Żenczykowski 	return 0;
275*1b481fc3SMaciej Żenczykowski }
276*1b481fc3SMaciej Żenczykowski 
get_stats_flag(struct nl_context * nlctx,unsigned int nlcmd,unsigned int hdrattr)277*1b481fc3SMaciej Żenczykowski u32 get_stats_flag(struct nl_context *nlctx, unsigned int nlcmd,
278*1b481fc3SMaciej Żenczykowski 		   unsigned int hdrattr)
279*1b481fc3SMaciej Żenczykowski {
280*1b481fc3SMaciej Żenczykowski 	if (!nlctx->ctx->show_stats)
281*1b481fc3SMaciej Żenczykowski 		return 0;
282*1b481fc3SMaciej Żenczykowski 	if (nlcmd > ETHTOOL_MSG_USER_MAX ||
283*1b481fc3SMaciej Żenczykowski 	    !(nlctx->ops_info[nlcmd].op_flags & GENL_CMD_CAP_HASPOL))
284*1b481fc3SMaciej Żenczykowski 		return 0;
285*1b481fc3SMaciej Żenczykowski 
286*1b481fc3SMaciej Żenczykowski 	if (read_flags_policy(nlctx, nlctx->ethnl_socket, nlcmd, hdrattr) < 0)
287*1b481fc3SMaciej Żenczykowski 		return 0;
288*1b481fc3SMaciej Żenczykowski 
289*1b481fc3SMaciej Żenczykowski 	return nlctx->ops_info[nlcmd].hdr_flags & ETHTOOL_FLAG_STATS;
290*1b481fc3SMaciej Żenczykowski }
291*1b481fc3SMaciej Żenczykowski 
292*1b481fc3SMaciej Żenczykowski /* initialization */
293*1b481fc3SMaciej Żenczykowski 
genl_read_ops(struct nl_context * nlctx,const struct nlattr * ops_attr)294*1b481fc3SMaciej Żenczykowski static int genl_read_ops(struct nl_context *nlctx,
295*1b481fc3SMaciej Żenczykowski 			 const struct nlattr *ops_attr)
296*1b481fc3SMaciej Żenczykowski {
297*1b481fc3SMaciej Żenczykowski 	struct nl_op_info *ops_info;
298*1b481fc3SMaciej Żenczykowski 	struct nlattr *op_attr;
299*1b481fc3SMaciej Żenczykowski 	int ret;
300*1b481fc3SMaciej Żenczykowski 
301*1b481fc3SMaciej Żenczykowski 	ops_info = calloc(__ETHTOOL_MSG_USER_CNT, sizeof(ops_info[0]));
302*1b481fc3SMaciej Żenczykowski 	if (!ops_info)
303*1b481fc3SMaciej Żenczykowski 		return -ENOMEM;
304*1b481fc3SMaciej Żenczykowski 
305*1b481fc3SMaciej Żenczykowski 	mnl_attr_for_each_nested(op_attr, ops_attr) {
306*1b481fc3SMaciej Żenczykowski 		const struct nlattr *tb[CTRL_ATTR_OP_MAX + 1] = {};
307*1b481fc3SMaciej Żenczykowski 		DECLARE_ATTR_TB_INFO(tb);
308*1b481fc3SMaciej Żenczykowski 		uint32_t op_id;
309*1b481fc3SMaciej Żenczykowski 
310*1b481fc3SMaciej Żenczykowski 		ret = mnl_attr_parse_nested(op_attr, attr_cb, &tb_info);
311*1b481fc3SMaciej Żenczykowski 		if (ret < 0)
312*1b481fc3SMaciej Żenczykowski 			goto err;
313*1b481fc3SMaciej Żenczykowski 
314*1b481fc3SMaciej Żenczykowski 		if (!tb[CTRL_ATTR_OP_ID] || !tb[CTRL_ATTR_OP_FLAGS])
315*1b481fc3SMaciej Żenczykowski 			continue;
316*1b481fc3SMaciej Żenczykowski 		op_id = mnl_attr_get_u32(tb[CTRL_ATTR_OP_ID]);
317*1b481fc3SMaciej Żenczykowski 		if (op_id >= __ETHTOOL_MSG_USER_CNT)
318*1b481fc3SMaciej Żenczykowski 			continue;
319*1b481fc3SMaciej Żenczykowski 
320*1b481fc3SMaciej Żenczykowski 		ops_info[op_id].op_flags =
321*1b481fc3SMaciej Żenczykowski 			mnl_attr_get_u32(tb[CTRL_ATTR_OP_FLAGS]);
322*1b481fc3SMaciej Żenczykowski 	}
323*1b481fc3SMaciej Żenczykowski 
324*1b481fc3SMaciej Żenczykowski 	nlctx->ops_info = ops_info;
325*1b481fc3SMaciej Żenczykowski 	return 0;
326*1b481fc3SMaciej Żenczykowski err:
327*1b481fc3SMaciej Żenczykowski 	free(ops_info);
328*1b481fc3SMaciej Żenczykowski 	return ret;
329*1b481fc3SMaciej Żenczykowski }
330*1b481fc3SMaciej Żenczykowski 
find_mc_group(struct nl_context * nlctx,struct nlattr * nest)331*1b481fc3SMaciej Żenczykowski static void find_mc_group(struct nl_context *nlctx, struct nlattr *nest)
332*1b481fc3SMaciej Żenczykowski {
333*1b481fc3SMaciej Żenczykowski 	const struct nlattr *grp_tb[CTRL_ATTR_MCAST_GRP_MAX + 1] = {};
334*1b481fc3SMaciej Żenczykowski 	DECLARE_ATTR_TB_INFO(grp_tb);
335*1b481fc3SMaciej Żenczykowski 	struct nlattr *grp_attr;
336*1b481fc3SMaciej Żenczykowski 	int ret;
337*1b481fc3SMaciej Żenczykowski 
338*1b481fc3SMaciej Żenczykowski 	mnl_attr_for_each_nested(grp_attr, nest) {
339*1b481fc3SMaciej Żenczykowski 		ret = mnl_attr_parse_nested(grp_attr, attr_cb, &grp_tb_info);
340*1b481fc3SMaciej Żenczykowski 		if (ret < 0)
341*1b481fc3SMaciej Żenczykowski 			return;
342*1b481fc3SMaciej Żenczykowski 		if (!grp_tb[CTRL_ATTR_MCAST_GRP_NAME] ||
343*1b481fc3SMaciej Żenczykowski 		    !grp_tb[CTRL_ATTR_MCAST_GRP_ID])
344*1b481fc3SMaciej Żenczykowski 			continue;
345*1b481fc3SMaciej Żenczykowski 		if (strcmp(mnl_attr_get_str(grp_tb[CTRL_ATTR_MCAST_GRP_NAME]),
346*1b481fc3SMaciej Żenczykowski 			   ETHTOOL_MCGRP_MONITOR_NAME))
347*1b481fc3SMaciej Żenczykowski 			continue;
348*1b481fc3SMaciej Żenczykowski 		nlctx->ethnl_mongrp =
349*1b481fc3SMaciej Żenczykowski 			mnl_attr_get_u32(grp_tb[CTRL_ATTR_MCAST_GRP_ID]);
350*1b481fc3SMaciej Żenczykowski 		return;
351*1b481fc3SMaciej Żenczykowski 	}
352*1b481fc3SMaciej Żenczykowski }
353*1b481fc3SMaciej Żenczykowski 
family_info_cb(const struct nlmsghdr * nlhdr,void * data)354*1b481fc3SMaciej Żenczykowski static int __maybe_unused family_info_cb(const struct nlmsghdr *nlhdr,
355*1b481fc3SMaciej Żenczykowski 					 void *data)
356*1b481fc3SMaciej Żenczykowski {
357*1b481fc3SMaciej Żenczykowski 	struct nl_context *nlctx = data;
358*1b481fc3SMaciej Żenczykowski 	struct nlattr *attr;
359*1b481fc3SMaciej Żenczykowski 	int ret;
360*1b481fc3SMaciej Żenczykowski 
361*1b481fc3SMaciej Żenczykowski 	mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN) {
362*1b481fc3SMaciej Żenczykowski 		switch (mnl_attr_get_type(attr)) {
363*1b481fc3SMaciej Żenczykowski 		case CTRL_ATTR_FAMILY_ID:
364*1b481fc3SMaciej Żenczykowski 			nlctx->ethnl_fam = mnl_attr_get_u16(attr);
365*1b481fc3SMaciej Żenczykowski 			break;
366*1b481fc3SMaciej Żenczykowski 		case CTRL_ATTR_OPS:
367*1b481fc3SMaciej Żenczykowski 			ret = genl_read_ops(nlctx, attr);
368*1b481fc3SMaciej Żenczykowski 			if (ret < 0)
369*1b481fc3SMaciej Żenczykowski 				return MNL_CB_ERROR;
370*1b481fc3SMaciej Żenczykowski 			break;
371*1b481fc3SMaciej Żenczykowski 		case CTRL_ATTR_MCAST_GROUPS:
372*1b481fc3SMaciej Żenczykowski 			find_mc_group(nlctx, attr);
373*1b481fc3SMaciej Żenczykowski 			break;
374*1b481fc3SMaciej Żenczykowski 		}
375*1b481fc3SMaciej Żenczykowski 	}
376*1b481fc3SMaciej Żenczykowski 
377*1b481fc3SMaciej Żenczykowski 	return MNL_CB_OK;
378*1b481fc3SMaciej Żenczykowski }
379*1b481fc3SMaciej Żenczykowski 
380*1b481fc3SMaciej Żenczykowski #ifdef TEST_ETHTOOL
get_genl_family(struct nl_context * nlctx __maybe_unused,struct nl_socket * nlsk __maybe_unused)381*1b481fc3SMaciej Żenczykowski static int get_genl_family(struct nl_context *nlctx __maybe_unused,
382*1b481fc3SMaciej Żenczykowski 			   struct nl_socket *nlsk __maybe_unused)
383*1b481fc3SMaciej Żenczykowski {
384*1b481fc3SMaciej Żenczykowski 	return 0;
385*1b481fc3SMaciej Żenczykowski }
386*1b481fc3SMaciej Żenczykowski #else
get_genl_family(struct nl_context * nlctx,struct nl_socket * nlsk)387*1b481fc3SMaciej Żenczykowski static int get_genl_family(struct nl_context *nlctx, struct nl_socket *nlsk)
388*1b481fc3SMaciej Żenczykowski {
389*1b481fc3SMaciej Żenczykowski 	struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
390*1b481fc3SMaciej Żenczykowski 	int ret;
391*1b481fc3SMaciej Żenczykowski 
392*1b481fc3SMaciej Żenczykowski 	nlctx->suppress_nlerr = 2;
393*1b481fc3SMaciej Żenczykowski 	ret = __msg_init(msgbuff, GENL_ID_CTRL, CTRL_CMD_GETFAMILY,
394*1b481fc3SMaciej Żenczykowski 			 NLM_F_REQUEST | NLM_F_ACK, 1);
395*1b481fc3SMaciej Żenczykowski 	if (ret < 0)
396*1b481fc3SMaciej Żenczykowski 		goto out;
397*1b481fc3SMaciej Żenczykowski 	ret = -EMSGSIZE;
398*1b481fc3SMaciej Żenczykowski 	if (ethnla_put_strz(msgbuff, CTRL_ATTR_FAMILY_NAME, ETHTOOL_GENL_NAME))
399*1b481fc3SMaciej Żenczykowski 		goto out;
400*1b481fc3SMaciej Żenczykowski 
401*1b481fc3SMaciej Żenczykowski 	nlsock_sendmsg(nlsk, NULL);
402*1b481fc3SMaciej Żenczykowski 	nlsock_process_reply(nlsk, family_info_cb, nlctx);
403*1b481fc3SMaciej Żenczykowski 	ret = nlctx->ethnl_fam ? 0 : -EADDRNOTAVAIL;
404*1b481fc3SMaciej Żenczykowski 
405*1b481fc3SMaciej Żenczykowski out:
406*1b481fc3SMaciej Żenczykowski 	nlctx->suppress_nlerr = 0;
407*1b481fc3SMaciej Żenczykowski 	return ret;
408*1b481fc3SMaciej Żenczykowski }
409*1b481fc3SMaciej Żenczykowski #endif
410*1b481fc3SMaciej Żenczykowski 
netlink_init(struct cmd_context * ctx)411*1b481fc3SMaciej Żenczykowski int netlink_init(struct cmd_context *ctx)
412*1b481fc3SMaciej Żenczykowski {
413*1b481fc3SMaciej Żenczykowski 	struct nl_context *nlctx;
414*1b481fc3SMaciej Żenczykowski 	int ret;
415*1b481fc3SMaciej Żenczykowski 
416*1b481fc3SMaciej Żenczykowski 	nlctx = calloc(1, sizeof(*nlctx));
417*1b481fc3SMaciej Żenczykowski 	if (!nlctx)
418*1b481fc3SMaciej Żenczykowski 		return -ENOMEM;
419*1b481fc3SMaciej Żenczykowski 	nlctx->ctx = ctx;
420*1b481fc3SMaciej Żenczykowski 	ret = nlsock_init(nlctx, &nlctx->ethnl_socket, NETLINK_GENERIC);
421*1b481fc3SMaciej Żenczykowski 	if (ret < 0)
422*1b481fc3SMaciej Żenczykowski 		goto out_free;
423*1b481fc3SMaciej Żenczykowski 	ret = get_genl_family(nlctx, nlctx->ethnl_socket);
424*1b481fc3SMaciej Żenczykowski 	if (ret < 0)
425*1b481fc3SMaciej Żenczykowski 		goto out_nlsk;
426*1b481fc3SMaciej Żenczykowski 
427*1b481fc3SMaciej Żenczykowski 	ctx->nlctx = nlctx;
428*1b481fc3SMaciej Żenczykowski 	return 0;
429*1b481fc3SMaciej Żenczykowski 
430*1b481fc3SMaciej Żenczykowski out_nlsk:
431*1b481fc3SMaciej Żenczykowski 	nlsock_done(nlctx->ethnl_socket);
432*1b481fc3SMaciej Żenczykowski out_free:
433*1b481fc3SMaciej Żenczykowski 	free(nlctx->ops_info);
434*1b481fc3SMaciej Żenczykowski 	free(nlctx);
435*1b481fc3SMaciej Żenczykowski 	return ret;
436*1b481fc3SMaciej Żenczykowski }
437*1b481fc3SMaciej Żenczykowski 
netlink_done(struct cmd_context * ctx)438*1b481fc3SMaciej Żenczykowski static void netlink_done(struct cmd_context *ctx)
439*1b481fc3SMaciej Żenczykowski {
440*1b481fc3SMaciej Żenczykowski 	struct nl_context *nlctx = ctx->nlctx;
441*1b481fc3SMaciej Żenczykowski 
442*1b481fc3SMaciej Żenczykowski 	if (!nlctx)
443*1b481fc3SMaciej Żenczykowski 		return;
444*1b481fc3SMaciej Żenczykowski 
445*1b481fc3SMaciej Żenczykowski 	nlsock_done(nlctx->ethnl_socket);
446*1b481fc3SMaciej Żenczykowski 	nlsock_done(nlctx->ethnl2_socket);
447*1b481fc3SMaciej Żenczykowski 	nlsock_done(nlctx->rtnl_socket);
448*1b481fc3SMaciej Żenczykowski 	free(nlctx->ops_info);
449*1b481fc3SMaciej Żenczykowski 	free(nlctx);
450*1b481fc3SMaciej Żenczykowski 	ctx->nlctx = NULL;
451*1b481fc3SMaciej Żenczykowski 	cleanup_all_strings();
452*1b481fc3SMaciej Żenczykowski }
453*1b481fc3SMaciej Żenczykowski 
454*1b481fc3SMaciej Żenczykowski /**
455*1b481fc3SMaciej Żenczykowski  * netlink_run_handler() - run netlink handler for subcommand
456*1b481fc3SMaciej Żenczykowski  * @ctx:         command context
457*1b481fc3SMaciej Żenczykowski  * @nlchk:       netlink capability check
458*1b481fc3SMaciej Żenczykowski  * @nlfunc:      subcommand netlink handler to call
459*1b481fc3SMaciej Żenczykowski  * @no_fallback: there is no ioctl fallback handler
460*1b481fc3SMaciej Żenczykowski  *
461*1b481fc3SMaciej Żenczykowski  * This function returns only if ioctl() handler should be run as fallback.
462*1b481fc3SMaciej Żenczykowski  * Otherwise it exits with appropriate return code.
463*1b481fc3SMaciej Żenczykowski  */
netlink_run_handler(struct cmd_context * ctx,nl_chk_t nlchk,nl_func_t nlfunc,bool no_fallback)464*1b481fc3SMaciej Żenczykowski void netlink_run_handler(struct cmd_context *ctx, nl_chk_t nlchk,
465*1b481fc3SMaciej Żenczykowski 			 nl_func_t nlfunc, bool no_fallback)
466*1b481fc3SMaciej Żenczykowski {
467*1b481fc3SMaciej Żenczykowski 	bool wildcard = ctx->devname && !strcmp(ctx->devname, WILDCARD_DEVNAME);
468*1b481fc3SMaciej Żenczykowski 	bool wildcard_unsupported, ioctl_fallback;
469*1b481fc3SMaciej Żenczykowski 	struct nl_context *nlctx;
470*1b481fc3SMaciej Żenczykowski 	const char *reason;
471*1b481fc3SMaciej Żenczykowski 	int ret;
472*1b481fc3SMaciej Żenczykowski 
473*1b481fc3SMaciej Żenczykowski 	if (nlchk && !nlchk(ctx)) {
474*1b481fc3SMaciej Żenczykowski 		reason = "ioctl-only request";
475*1b481fc3SMaciej Żenczykowski 		goto no_support;
476*1b481fc3SMaciej Żenczykowski 	}
477*1b481fc3SMaciej Żenczykowski 	if (ctx->devname && strlen(ctx->devname) >= ALTIFNAMSIZ) {
478*1b481fc3SMaciej Żenczykowski 		fprintf(stderr, "device name '%s' longer than %u characters\n",
479*1b481fc3SMaciej Żenczykowski 			ctx->devname, ALTIFNAMSIZ - 1);
480*1b481fc3SMaciej Żenczykowski 		exit(1);
481*1b481fc3SMaciej Żenczykowski 	}
482*1b481fc3SMaciej Żenczykowski 
483*1b481fc3SMaciej Żenczykowski 	if (!nlfunc) {
484*1b481fc3SMaciej Żenczykowski 		reason = "ethtool netlink support for subcommand missing";
485*1b481fc3SMaciej Żenczykowski 		goto no_support;
486*1b481fc3SMaciej Żenczykowski 	}
487*1b481fc3SMaciej Żenczykowski 	if (netlink_init(ctx)) {
488*1b481fc3SMaciej Żenczykowski 		reason = "netlink interface initialization failed";
489*1b481fc3SMaciej Żenczykowski 		goto no_support;
490*1b481fc3SMaciej Żenczykowski 	}
491*1b481fc3SMaciej Żenczykowski 	nlctx = ctx->nlctx;
492*1b481fc3SMaciej Żenczykowski 
493*1b481fc3SMaciej Żenczykowski 	ret = nlfunc(ctx);
494*1b481fc3SMaciej Żenczykowski 	wildcard_unsupported = nlctx->wildcard_unsupported;
495*1b481fc3SMaciej Żenczykowski 	ioctl_fallback = nlctx->ioctl_fallback;
496*1b481fc3SMaciej Żenczykowski 	netlink_done(ctx);
497*1b481fc3SMaciej Żenczykowski 
498*1b481fc3SMaciej Żenczykowski 	if (no_fallback || ret != -EOPNOTSUPP || !ioctl_fallback) {
499*1b481fc3SMaciej Żenczykowski 		if (wildcard_unsupported)
500*1b481fc3SMaciej Żenczykowski 			fprintf(stderr, "%s\n",
501*1b481fc3SMaciej Żenczykowski 				"subcommand does not support wildcard dump");
502*1b481fc3SMaciej Żenczykowski 		exit(ret >= 0 ? ret : 1);
503*1b481fc3SMaciej Żenczykowski 	}
504*1b481fc3SMaciej Żenczykowski 	if (wildcard_unsupported)
505*1b481fc3SMaciej Żenczykowski 		reason = "subcommand does not support wildcard dump";
506*1b481fc3SMaciej Żenczykowski 	else
507*1b481fc3SMaciej Żenczykowski 		reason = "kernel netlink support for subcommand missing";
508*1b481fc3SMaciej Żenczykowski 
509*1b481fc3SMaciej Żenczykowski no_support:
510*1b481fc3SMaciej Żenczykowski 	if (no_fallback) {
511*1b481fc3SMaciej Żenczykowski 		fprintf(stderr, "%s, subcommand not supported by ioctl\n",
512*1b481fc3SMaciej Żenczykowski 			reason);
513*1b481fc3SMaciej Żenczykowski 		exit(1);
514*1b481fc3SMaciej Żenczykowski 	}
515*1b481fc3SMaciej Żenczykowski 	if (wildcard) {
516*1b481fc3SMaciej Żenczykowski 		fprintf(stderr, "%s, wildcard dump not supported\n", reason);
517*1b481fc3SMaciej Żenczykowski 		exit(1);
518*1b481fc3SMaciej Żenczykowski 	}
519*1b481fc3SMaciej Żenczykowski 	if (ctx->devname && strlen(ctx->devname) >= IFNAMSIZ) {
520*1b481fc3SMaciej Żenczykowski 		fprintf(stderr,
521*1b481fc3SMaciej Żenczykowski 			"%s, device name longer than %u not supported\n",
522*1b481fc3SMaciej Żenczykowski 			reason, IFNAMSIZ - 1);
523*1b481fc3SMaciej Żenczykowski 		exit(1);
524*1b481fc3SMaciej Żenczykowski 	}
525*1b481fc3SMaciej Żenczykowski 
526*1b481fc3SMaciej Żenczykowski 	/* fallback to ioctl() */
527*1b481fc3SMaciej Żenczykowski }
528