1*1b481fc3SMaciej Żenczykowski /*
2*1b481fc3SMaciej Żenczykowski * features.c - netlink implementation of netdev features commands
3*1b481fc3SMaciej Żenczykowski *
4*1b481fc3SMaciej Żenczykowski * Implementation of "ethtool -k <dev>".
5*1b481fc3SMaciej Żenczykowski */
6*1b481fc3SMaciej Żenczykowski
7*1b481fc3SMaciej Żenczykowski #include <errno.h>
8*1b481fc3SMaciej Żenczykowski #include <string.h>
9*1b481fc3SMaciej Żenczykowski #include <stdio.h>
10*1b481fc3SMaciej Żenczykowski
11*1b481fc3SMaciej Żenczykowski #include "../internal.h"
12*1b481fc3SMaciej Żenczykowski #include "../common.h"
13*1b481fc3SMaciej Żenczykowski #include "netlink.h"
14*1b481fc3SMaciej Żenczykowski #include "strset.h"
15*1b481fc3SMaciej Żenczykowski #include "bitset.h"
16*1b481fc3SMaciej Żenczykowski
17*1b481fc3SMaciej Żenczykowski /* FEATURES_GET */
18*1b481fc3SMaciej Żenczykowski
19*1b481fc3SMaciej Żenczykowski struct feature_results {
20*1b481fc3SMaciej Żenczykowski uint32_t *hw;
21*1b481fc3SMaciej Żenczykowski uint32_t *wanted;
22*1b481fc3SMaciej Żenczykowski uint32_t *active;
23*1b481fc3SMaciej Żenczykowski uint32_t *nochange;
24*1b481fc3SMaciej Żenczykowski unsigned int count;
25*1b481fc3SMaciej Żenczykowski unsigned int words;
26*1b481fc3SMaciej Żenczykowski };
27*1b481fc3SMaciej Żenczykowski
prepare_feature_results(const struct nlattr * const * tb,struct feature_results * dest)28*1b481fc3SMaciej Żenczykowski static int prepare_feature_results(const struct nlattr *const *tb,
29*1b481fc3SMaciej Żenczykowski struct feature_results *dest)
30*1b481fc3SMaciej Żenczykowski {
31*1b481fc3SMaciej Żenczykowski unsigned int count;
32*1b481fc3SMaciej Żenczykowski int ret;
33*1b481fc3SMaciej Żenczykowski
34*1b481fc3SMaciej Żenczykowski memset(dest, '\0', sizeof(*dest));
35*1b481fc3SMaciej Żenczykowski if (!tb[ETHTOOL_A_FEATURES_HW] || !tb[ETHTOOL_A_FEATURES_WANTED] ||
36*1b481fc3SMaciej Żenczykowski !tb[ETHTOOL_A_FEATURES_ACTIVE] || !tb[ETHTOOL_A_FEATURES_NOCHANGE])
37*1b481fc3SMaciej Żenczykowski return -EFAULT;
38*1b481fc3SMaciej Żenczykowski count = bitset_get_count(tb[ETHTOOL_A_FEATURES_HW], &ret);
39*1b481fc3SMaciej Żenczykowski if (ret < 0)
40*1b481fc3SMaciej Żenczykowski return -EFAULT;
41*1b481fc3SMaciej Żenczykowski if ((bitset_get_count(tb[ETHTOOL_A_FEATURES_WANTED], &ret) != count) ||
42*1b481fc3SMaciej Żenczykowski (bitset_get_count(tb[ETHTOOL_A_FEATURES_ACTIVE], &ret) != count) ||
43*1b481fc3SMaciej Żenczykowski (bitset_get_count(tb[ETHTOOL_A_FEATURES_NOCHANGE], &ret) != count))
44*1b481fc3SMaciej Żenczykowski return -EFAULT;
45*1b481fc3SMaciej Żenczykowski dest->hw = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_HW]);
46*1b481fc3SMaciej Żenczykowski dest->wanted = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_WANTED]);
47*1b481fc3SMaciej Żenczykowski dest->active = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_ACTIVE]);
48*1b481fc3SMaciej Żenczykowski dest->nochange =
49*1b481fc3SMaciej Żenczykowski get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_NOCHANGE]);
50*1b481fc3SMaciej Żenczykowski if (!dest->hw || !dest->wanted || !dest->active || !dest->nochange)
51*1b481fc3SMaciej Żenczykowski return -EFAULT;
52*1b481fc3SMaciej Żenczykowski dest->count = count;
53*1b481fc3SMaciej Żenczykowski dest->words = (count + 31) / 32;
54*1b481fc3SMaciej Żenczykowski
55*1b481fc3SMaciej Żenczykowski return 0;
56*1b481fc3SMaciej Żenczykowski }
57*1b481fc3SMaciej Żenczykowski
feature_on(const uint32_t * bitmap,unsigned int idx)58*1b481fc3SMaciej Żenczykowski static bool feature_on(const uint32_t *bitmap, unsigned int idx)
59*1b481fc3SMaciej Żenczykowski {
60*1b481fc3SMaciej Żenczykowski return bitmap[idx / 32] & (1 << (idx % 32));
61*1b481fc3SMaciej Żenczykowski }
62*1b481fc3SMaciej Żenczykowski
dump_feature(const struct feature_results * results,const uint32_t * ref,const uint32_t * ref_mask,unsigned int idx,const char * name,const char * prefix)63*1b481fc3SMaciej Żenczykowski static void dump_feature(const struct feature_results *results,
64*1b481fc3SMaciej Żenczykowski const uint32_t *ref, const uint32_t *ref_mask,
65*1b481fc3SMaciej Żenczykowski unsigned int idx, const char *name, const char *prefix)
66*1b481fc3SMaciej Żenczykowski {
67*1b481fc3SMaciej Żenczykowski const char *suffix = "";
68*1b481fc3SMaciej Żenczykowski
69*1b481fc3SMaciej Żenczykowski if (!name || !*name)
70*1b481fc3SMaciej Żenczykowski return;
71*1b481fc3SMaciej Żenczykowski if (ref) {
72*1b481fc3SMaciej Żenczykowski if (ref_mask && !feature_on(ref_mask, idx))
73*1b481fc3SMaciej Żenczykowski return;
74*1b481fc3SMaciej Żenczykowski if ((!ref_mask || feature_on(ref_mask, idx)) &&
75*1b481fc3SMaciej Żenczykowski (feature_on(results->active, idx) == feature_on(ref, idx)))
76*1b481fc3SMaciej Żenczykowski return;
77*1b481fc3SMaciej Żenczykowski }
78*1b481fc3SMaciej Żenczykowski
79*1b481fc3SMaciej Żenczykowski if (!feature_on(results->hw, idx) || feature_on(results->nochange, idx))
80*1b481fc3SMaciej Żenczykowski suffix = " [fixed]";
81*1b481fc3SMaciej Żenczykowski else if (feature_on(results->active, idx) !=
82*1b481fc3SMaciej Żenczykowski feature_on(results->wanted, idx))
83*1b481fc3SMaciej Żenczykowski suffix = feature_on(results->wanted, idx) ?
84*1b481fc3SMaciej Żenczykowski " [requested on]" : " [requested off]";
85*1b481fc3SMaciej Żenczykowski if (is_json_context()) {
86*1b481fc3SMaciej Żenczykowski open_json_object(name);
87*1b481fc3SMaciej Żenczykowski print_bool(PRINT_JSON, "active", NULL, feature_on(results->active, idx));
88*1b481fc3SMaciej Żenczykowski print_bool(PRINT_JSON, "fixed", NULL,
89*1b481fc3SMaciej Żenczykowski (!feature_on(results->hw, idx) || feature_on(results->nochange, idx)));
90*1b481fc3SMaciej Żenczykowski print_bool(PRINT_JSON, "requested", NULL, feature_on(results->wanted, idx));
91*1b481fc3SMaciej Żenczykowski close_json_object();
92*1b481fc3SMaciej Żenczykowski } else {
93*1b481fc3SMaciej Żenczykowski printf("%s%s: %s%s\n", prefix, name,
94*1b481fc3SMaciej Żenczykowski feature_on(results->active, idx) ? "on" : "off", suffix);
95*1b481fc3SMaciej Żenczykowski }
96*1b481fc3SMaciej Żenczykowski }
97*1b481fc3SMaciej Żenczykowski
98*1b481fc3SMaciej Żenczykowski /* this assumes pattern contains no more than one asterisk */
flag_pattern_match(const char * name,const char * pattern)99*1b481fc3SMaciej Żenczykowski static bool flag_pattern_match(const char *name, const char *pattern)
100*1b481fc3SMaciej Żenczykowski {
101*1b481fc3SMaciej Żenczykowski const char *p_ast = strchr(pattern, '*');
102*1b481fc3SMaciej Żenczykowski
103*1b481fc3SMaciej Żenczykowski if (p_ast) {
104*1b481fc3SMaciej Żenczykowski size_t name_len = strlen(name);
105*1b481fc3SMaciej Żenczykowski size_t pattern_len = strlen(pattern);
106*1b481fc3SMaciej Żenczykowski
107*1b481fc3SMaciej Żenczykowski if (name_len + 1 < pattern_len)
108*1b481fc3SMaciej Żenczykowski return false;
109*1b481fc3SMaciej Żenczykowski if (strncmp(name, pattern, p_ast - pattern))
110*1b481fc3SMaciej Żenczykowski return false;
111*1b481fc3SMaciej Żenczykowski pattern_len -= (p_ast - pattern) + 1;
112*1b481fc3SMaciej Żenczykowski name += name_len - pattern_len;
113*1b481fc3SMaciej Żenczykowski pattern = p_ast + 1;
114*1b481fc3SMaciej Żenczykowski }
115*1b481fc3SMaciej Żenczykowski return !strcmp(name, pattern);
116*1b481fc3SMaciej Żenczykowski }
117*1b481fc3SMaciej Żenczykowski
dump_features(const struct nlattr * const * tb,const struct stringset * feature_names)118*1b481fc3SMaciej Żenczykowski int dump_features(const struct nlattr *const *tb,
119*1b481fc3SMaciej Żenczykowski const struct stringset *feature_names)
120*1b481fc3SMaciej Żenczykowski {
121*1b481fc3SMaciej Żenczykowski unsigned int *feature_flags = NULL;
122*1b481fc3SMaciej Żenczykowski struct feature_results results;
123*1b481fc3SMaciej Żenczykowski unsigned int i, j;
124*1b481fc3SMaciej Żenczykowski int ret;
125*1b481fc3SMaciej Żenczykowski
126*1b481fc3SMaciej Żenczykowski ret = prepare_feature_results(tb, &results);
127*1b481fc3SMaciej Żenczykowski if (ret < 0)
128*1b481fc3SMaciej Żenczykowski return -EFAULT;
129*1b481fc3SMaciej Żenczykowski feature_flags = calloc(results.count, sizeof(feature_flags[0]));
130*1b481fc3SMaciej Żenczykowski if (!feature_flags)
131*1b481fc3SMaciej Żenczykowski return -ENOMEM;
132*1b481fc3SMaciej Żenczykowski
133*1b481fc3SMaciej Żenczykowski /* map netdev features to legacy flags */
134*1b481fc3SMaciej Żenczykowski for (i = 0; i < results.count; i++) {
135*1b481fc3SMaciej Żenczykowski const char *name = get_string(feature_names, i);
136*1b481fc3SMaciej Żenczykowski feature_flags[i] = UINT_MAX;
137*1b481fc3SMaciej Żenczykowski
138*1b481fc3SMaciej Żenczykowski if (!name || !*name)
139*1b481fc3SMaciej Żenczykowski continue;
140*1b481fc3SMaciej Żenczykowski for (j = 0; j < OFF_FLAG_DEF_SIZE; j++) {
141*1b481fc3SMaciej Żenczykowski const char *flag_name = off_flag_def[j].kernel_name;
142*1b481fc3SMaciej Żenczykowski
143*1b481fc3SMaciej Żenczykowski if (flag_pattern_match(name, flag_name)) {
144*1b481fc3SMaciej Żenczykowski feature_flags[i] = j;
145*1b481fc3SMaciej Żenczykowski break;
146*1b481fc3SMaciej Żenczykowski }
147*1b481fc3SMaciej Żenczykowski }
148*1b481fc3SMaciej Żenczykowski }
149*1b481fc3SMaciej Żenczykowski /* show legacy flags and their matching features first */
150*1b481fc3SMaciej Żenczykowski for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
151*1b481fc3SMaciej Żenczykowski unsigned int n_match = 0;
152*1b481fc3SMaciej Żenczykowski bool flag_value = false;
153*1b481fc3SMaciej Żenczykowski
154*1b481fc3SMaciej Żenczykowski /* no kernel with netlink interface supports UFO */
155*1b481fc3SMaciej Żenczykowski if (off_flag_def[i].value == ETH_FLAG_UFO)
156*1b481fc3SMaciej Żenczykowski continue;
157*1b481fc3SMaciej Żenczykowski
158*1b481fc3SMaciej Żenczykowski for (j = 0; j < results.count; j++) {
159*1b481fc3SMaciej Żenczykowski if (feature_flags[j] == i) {
160*1b481fc3SMaciej Żenczykowski n_match++;
161*1b481fc3SMaciej Żenczykowski flag_value = flag_value ||
162*1b481fc3SMaciej Żenczykowski feature_on(results.active, j);
163*1b481fc3SMaciej Żenczykowski }
164*1b481fc3SMaciej Żenczykowski }
165*1b481fc3SMaciej Żenczykowski if (n_match != 1) {
166*1b481fc3SMaciej Żenczykowski if (is_json_context()) {
167*1b481fc3SMaciej Żenczykowski open_json_object(off_flag_def[i].long_name);
168*1b481fc3SMaciej Żenczykowski print_bool(PRINT_JSON, "active", NULL, flag_value);
169*1b481fc3SMaciej Żenczykowski print_null(PRINT_JSON, "fixed", NULL, NULL);
170*1b481fc3SMaciej Żenczykowski print_null(PRINT_JSON, "requested", NULL, NULL);
171*1b481fc3SMaciej Żenczykowski close_json_object();
172*1b481fc3SMaciej Żenczykowski } else {
173*1b481fc3SMaciej Żenczykowski printf("%s: %s\n", off_flag_def[i].long_name,
174*1b481fc3SMaciej Żenczykowski flag_value ? "on" : "off");
175*1b481fc3SMaciej Żenczykowski }
176*1b481fc3SMaciej Żenczykowski }
177*1b481fc3SMaciej Żenczykowski if (n_match == 0)
178*1b481fc3SMaciej Żenczykowski continue;
179*1b481fc3SMaciej Żenczykowski for (j = 0; j < results.count; j++) {
180*1b481fc3SMaciej Żenczykowski const char *name = get_string(feature_names, j);
181*1b481fc3SMaciej Żenczykowski
182*1b481fc3SMaciej Żenczykowski if (feature_flags[j] != i)
183*1b481fc3SMaciej Żenczykowski continue;
184*1b481fc3SMaciej Żenczykowski if (n_match == 1)
185*1b481fc3SMaciej Żenczykowski dump_feature(&results, NULL, NULL, j,
186*1b481fc3SMaciej Żenczykowski off_flag_def[i].long_name, "");
187*1b481fc3SMaciej Żenczykowski else
188*1b481fc3SMaciej Żenczykowski dump_feature(&results, NULL, NULL, j, name,
189*1b481fc3SMaciej Żenczykowski "\t");
190*1b481fc3SMaciej Żenczykowski }
191*1b481fc3SMaciej Żenczykowski }
192*1b481fc3SMaciej Żenczykowski /* and, finally, remaining netdev_features not matching legacy flags */
193*1b481fc3SMaciej Żenczykowski for (i = 0; i < results.count; i++) {
194*1b481fc3SMaciej Żenczykowski const char *name = get_string(feature_names, i);
195*1b481fc3SMaciej Żenczykowski
196*1b481fc3SMaciej Żenczykowski if (!name || !*name || feature_flags[i] != UINT_MAX)
197*1b481fc3SMaciej Żenczykowski continue;
198*1b481fc3SMaciej Żenczykowski dump_feature(&results, NULL, NULL, i, name, "");
199*1b481fc3SMaciej Żenczykowski }
200*1b481fc3SMaciej Żenczykowski
201*1b481fc3SMaciej Żenczykowski free(feature_flags);
202*1b481fc3SMaciej Żenczykowski return 0;
203*1b481fc3SMaciej Żenczykowski }
204*1b481fc3SMaciej Żenczykowski
features_reply_cb(const struct nlmsghdr * nlhdr,void * data)205*1b481fc3SMaciej Żenczykowski int features_reply_cb(const struct nlmsghdr *nlhdr, void *data)
206*1b481fc3SMaciej Żenczykowski {
207*1b481fc3SMaciej Żenczykowski const struct nlattr *tb[ETHTOOL_A_FEATURES_MAX + 1] = {};
208*1b481fc3SMaciej Żenczykowski DECLARE_ATTR_TB_INFO(tb);
209*1b481fc3SMaciej Żenczykowski const struct stringset *feature_names;
210*1b481fc3SMaciej Żenczykowski struct nl_context *nlctx = data;
211*1b481fc3SMaciej Żenczykowski bool silent;
212*1b481fc3SMaciej Żenczykowski int ret;
213*1b481fc3SMaciej Żenczykowski
214*1b481fc3SMaciej Żenczykowski silent = nlctx->is_dump || nlctx->is_monitor;
215*1b481fc3SMaciej Żenczykowski if (!nlctx->is_monitor) {
216*1b481fc3SMaciej Żenczykowski ret = netlink_init_ethnl2_socket(nlctx);
217*1b481fc3SMaciej Żenczykowski if (ret < 0)
218*1b481fc3SMaciej Żenczykowski return MNL_CB_ERROR;
219*1b481fc3SMaciej Żenczykowski }
220*1b481fc3SMaciej Żenczykowski feature_names = global_stringset(ETH_SS_FEATURES, nlctx->ethnl2_socket);
221*1b481fc3SMaciej Żenczykowski
222*1b481fc3SMaciej Żenczykowski ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
223*1b481fc3SMaciej Żenczykowski if (ret < 0)
224*1b481fc3SMaciej Żenczykowski return silent ? MNL_CB_OK : MNL_CB_ERROR;
225*1b481fc3SMaciej Żenczykowski nlctx->devname = get_dev_name(tb[ETHTOOL_A_FEATURES_HEADER]);
226*1b481fc3SMaciej Żenczykowski if (!dev_ok(nlctx))
227*1b481fc3SMaciej Żenczykowski return MNL_CB_OK;
228*1b481fc3SMaciej Żenczykowski
229*1b481fc3SMaciej Żenczykowski if (silent)
230*1b481fc3SMaciej Żenczykowski putchar('\n');
231*1b481fc3SMaciej Żenczykowski open_json_object(NULL);
232*1b481fc3SMaciej Żenczykowski print_string(PRINT_ANY, "ifname", "Features for %s:\n", nlctx->devname);
233*1b481fc3SMaciej Żenczykowski ret = dump_features(tb, feature_names);
234*1b481fc3SMaciej Żenczykowski close_json_object();
235*1b481fc3SMaciej Żenczykowski return (silent || !ret) ? MNL_CB_OK : MNL_CB_ERROR;
236*1b481fc3SMaciej Żenczykowski }
237*1b481fc3SMaciej Żenczykowski
nl_gfeatures(struct cmd_context * ctx)238*1b481fc3SMaciej Żenczykowski int nl_gfeatures(struct cmd_context *ctx)
239*1b481fc3SMaciej Żenczykowski {
240*1b481fc3SMaciej Żenczykowski struct nl_context *nlctx = ctx->nlctx;
241*1b481fc3SMaciej Żenczykowski struct nl_socket *nlsk = nlctx->ethnl_socket;
242*1b481fc3SMaciej Żenczykowski int ret;
243*1b481fc3SMaciej Żenczykowski
244*1b481fc3SMaciej Żenczykowski if (netlink_cmd_check(ctx, ETHTOOL_MSG_FEATURES_GET, true))
245*1b481fc3SMaciej Żenczykowski return -EOPNOTSUPP;
246*1b481fc3SMaciej Żenczykowski if (ctx->argc > 0) {
247*1b481fc3SMaciej Żenczykowski fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
248*1b481fc3SMaciej Żenczykowski *ctx->argp);
249*1b481fc3SMaciej Żenczykowski return 1;
250*1b481fc3SMaciej Żenczykowski }
251*1b481fc3SMaciej Żenczykowski
252*1b481fc3SMaciej Żenczykowski ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_FEATURES_GET,
253*1b481fc3SMaciej Żenczykowski ETHTOOL_A_FEATURES_HEADER,
254*1b481fc3SMaciej Żenczykowski ETHTOOL_FLAG_COMPACT_BITSETS);
255*1b481fc3SMaciej Żenczykowski if (ret < 0)
256*1b481fc3SMaciej Żenczykowski return ret;
257*1b481fc3SMaciej Żenczykowski
258*1b481fc3SMaciej Żenczykowski new_json_obj(ctx->json);
259*1b481fc3SMaciej Żenczykowski ret = nlsock_send_get_request(nlsk, features_reply_cb);
260*1b481fc3SMaciej Żenczykowski delete_json_obj();
261*1b481fc3SMaciej Żenczykowski
262*1b481fc3SMaciej Żenczykowski return ret;
263*1b481fc3SMaciej Żenczykowski }
264*1b481fc3SMaciej Żenczykowski
265*1b481fc3SMaciej Żenczykowski /* FEATURES_SET */
266*1b481fc3SMaciej Żenczykowski
267*1b481fc3SMaciej Żenczykowski struct sfeatures_context {
268*1b481fc3SMaciej Żenczykowski bool nothing_changed;
269*1b481fc3SMaciej Żenczykowski uint32_t req_mask[];
270*1b481fc3SMaciej Żenczykowski };
271*1b481fc3SMaciej Żenczykowski
find_feature(const char * name,const struct stringset * feature_names)272*1b481fc3SMaciej Żenczykowski static int find_feature(const char *name,
273*1b481fc3SMaciej Żenczykowski const struct stringset *feature_names)
274*1b481fc3SMaciej Żenczykowski {
275*1b481fc3SMaciej Żenczykowski const unsigned int count = get_count(feature_names);
276*1b481fc3SMaciej Żenczykowski unsigned int i;
277*1b481fc3SMaciej Żenczykowski
278*1b481fc3SMaciej Żenczykowski for (i = 0; i < count; i++)
279*1b481fc3SMaciej Żenczykowski if (!strcmp(name, get_string(feature_names, i)))
280*1b481fc3SMaciej Żenczykowski return i;
281*1b481fc3SMaciej Żenczykowski
282*1b481fc3SMaciej Żenczykowski return -1;
283*1b481fc3SMaciej Żenczykowski }
284*1b481fc3SMaciej Żenczykowski
fill_feature(struct nl_msg_buff * msgbuff,const char * name,bool val)285*1b481fc3SMaciej Żenczykowski static int fill_feature(struct nl_msg_buff *msgbuff, const char *name, bool val)
286*1b481fc3SMaciej Żenczykowski {
287*1b481fc3SMaciej Żenczykowski struct nlattr *bit_attr;
288*1b481fc3SMaciej Żenczykowski
289*1b481fc3SMaciej Żenczykowski bit_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS_BIT);
290*1b481fc3SMaciej Żenczykowski if (!bit_attr)
291*1b481fc3SMaciej Żenczykowski return -EMSGSIZE;
292*1b481fc3SMaciej Żenczykowski if (ethnla_put_strz(msgbuff, ETHTOOL_A_BITSET_BIT_NAME, name))
293*1b481fc3SMaciej Żenczykowski return -EMSGSIZE;
294*1b481fc3SMaciej Żenczykowski if (ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_BIT_VALUE, val))
295*1b481fc3SMaciej Żenczykowski return -EMSGSIZE;
296*1b481fc3SMaciej Żenczykowski mnl_attr_nest_end(msgbuff->nlhdr, bit_attr);
297*1b481fc3SMaciej Żenczykowski
298*1b481fc3SMaciej Żenczykowski return 0;
299*1b481fc3SMaciej Żenczykowski }
300*1b481fc3SMaciej Żenczykowski
set_sf_req_mask(struct nl_context * nlctx,unsigned int idx)301*1b481fc3SMaciej Żenczykowski static void set_sf_req_mask(struct nl_context *nlctx, unsigned int idx)
302*1b481fc3SMaciej Żenczykowski {
303*1b481fc3SMaciej Żenczykowski struct sfeatures_context *sfctx = nlctx->cmd_private;
304*1b481fc3SMaciej Żenczykowski
305*1b481fc3SMaciej Żenczykowski sfctx->req_mask[idx / 32] |= (1 << (idx % 32));
306*1b481fc3SMaciej Żenczykowski }
307*1b481fc3SMaciej Żenczykowski
fill_legacy_flag(struct nl_context * nlctx,const char * flag_name,const struct stringset * feature_names,bool val)308*1b481fc3SMaciej Żenczykowski static int fill_legacy_flag(struct nl_context *nlctx, const char *flag_name,
309*1b481fc3SMaciej Żenczykowski const struct stringset *feature_names, bool val)
310*1b481fc3SMaciej Żenczykowski {
311*1b481fc3SMaciej Żenczykowski struct nl_msg_buff *msgbuff = &nlctx->ethnl_socket->msgbuff;
312*1b481fc3SMaciej Żenczykowski const unsigned int count = get_count(feature_names);
313*1b481fc3SMaciej Żenczykowski unsigned int i, j;
314*1b481fc3SMaciej Żenczykowski int ret;
315*1b481fc3SMaciej Żenczykowski
316*1b481fc3SMaciej Żenczykowski for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
317*1b481fc3SMaciej Żenczykowski const char *pattern;
318*1b481fc3SMaciej Żenczykowski
319*1b481fc3SMaciej Żenczykowski if (strcmp(flag_name, off_flag_def[i].short_name) &&
320*1b481fc3SMaciej Żenczykowski strcmp(flag_name, off_flag_def[i].long_name))
321*1b481fc3SMaciej Żenczykowski continue;
322*1b481fc3SMaciej Żenczykowski pattern = off_flag_def[i].kernel_name;
323*1b481fc3SMaciej Żenczykowski
324*1b481fc3SMaciej Żenczykowski for (j = 0; j < count; j++) {
325*1b481fc3SMaciej Żenczykowski const char *name = get_string(feature_names, j);
326*1b481fc3SMaciej Żenczykowski
327*1b481fc3SMaciej Żenczykowski if (flag_pattern_match(name, pattern)) {
328*1b481fc3SMaciej Żenczykowski ret = fill_feature(msgbuff, name, val);
329*1b481fc3SMaciej Żenczykowski if (ret < 0)
330*1b481fc3SMaciej Żenczykowski return ret;
331*1b481fc3SMaciej Żenczykowski set_sf_req_mask(nlctx, j);
332*1b481fc3SMaciej Żenczykowski }
333*1b481fc3SMaciej Żenczykowski }
334*1b481fc3SMaciej Żenczykowski
335*1b481fc3SMaciej Żenczykowski return 0;
336*1b481fc3SMaciej Żenczykowski }
337*1b481fc3SMaciej Żenczykowski
338*1b481fc3SMaciej Żenczykowski return 1;
339*1b481fc3SMaciej Żenczykowski }
340*1b481fc3SMaciej Żenczykowski
fill_sfeatures_bitmap(struct nl_context * nlctx,const struct stringset * feature_names)341*1b481fc3SMaciej Żenczykowski int fill_sfeatures_bitmap(struct nl_context *nlctx,
342*1b481fc3SMaciej Żenczykowski const struct stringset *feature_names)
343*1b481fc3SMaciej Żenczykowski {
344*1b481fc3SMaciej Żenczykowski struct nl_msg_buff *msgbuff = &nlctx->ethnl_socket->msgbuff;
345*1b481fc3SMaciej Żenczykowski struct nlattr *bitset_attr;
346*1b481fc3SMaciej Żenczykowski struct nlattr *bits_attr;
347*1b481fc3SMaciej Żenczykowski int ret;
348*1b481fc3SMaciej Żenczykowski
349*1b481fc3SMaciej Żenczykowski ret = -EMSGSIZE;
350*1b481fc3SMaciej Żenczykowski bitset_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_FEATURES_WANTED);
351*1b481fc3SMaciej Żenczykowski if (!bitset_attr)
352*1b481fc3SMaciej Żenczykowski return ret;
353*1b481fc3SMaciej Żenczykowski bits_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS);
354*1b481fc3SMaciej Żenczykowski if (!bits_attr)
355*1b481fc3SMaciej Żenczykowski goto err;
356*1b481fc3SMaciej Żenczykowski
357*1b481fc3SMaciej Żenczykowski while (nlctx->argc > 0) {
358*1b481fc3SMaciej Żenczykowski bool val;
359*1b481fc3SMaciej Żenczykowski
360*1b481fc3SMaciej Żenczykowski if (!strcmp(*nlctx->argp, "--")) {
361*1b481fc3SMaciej Żenczykowski nlctx->argp++;
362*1b481fc3SMaciej Żenczykowski nlctx->argc--;
363*1b481fc3SMaciej Żenczykowski break;
364*1b481fc3SMaciej Żenczykowski }
365*1b481fc3SMaciej Żenczykowski ret = -EINVAL;
366*1b481fc3SMaciej Żenczykowski if (nlctx->argc < 2 ||
367*1b481fc3SMaciej Żenczykowski (strcmp(nlctx->argp[1], "on") &&
368*1b481fc3SMaciej Żenczykowski strcmp(nlctx->argp[1], "off"))) {
369*1b481fc3SMaciej Żenczykowski fprintf(stderr,
370*1b481fc3SMaciej Żenczykowski "ethtool (%s): flag '%s' for parameter '%s' is"
371*1b481fc3SMaciej Żenczykowski " not followed by 'on' or 'off'\n",
372*1b481fc3SMaciej Żenczykowski nlctx->cmd, nlctx->argp[1], nlctx->param);
373*1b481fc3SMaciej Żenczykowski goto err;
374*1b481fc3SMaciej Żenczykowski }
375*1b481fc3SMaciej Żenczykowski
376*1b481fc3SMaciej Żenczykowski val = !strcmp(nlctx->argp[1], "on");
377*1b481fc3SMaciej Żenczykowski ret = fill_legacy_flag(nlctx, nlctx->argp[0], feature_names,
378*1b481fc3SMaciej Żenczykowski val);
379*1b481fc3SMaciej Żenczykowski if (ret > 0) {
380*1b481fc3SMaciej Żenczykowski ret = fill_feature(msgbuff, nlctx->argp[0], val);
381*1b481fc3SMaciej Żenczykowski if (ret == 0) {
382*1b481fc3SMaciej Żenczykowski int idx = find_feature(nlctx->argp[0],
383*1b481fc3SMaciej Żenczykowski feature_names);
384*1b481fc3SMaciej Żenczykowski
385*1b481fc3SMaciej Żenczykowski if (idx >= 0)
386*1b481fc3SMaciej Żenczykowski set_sf_req_mask(nlctx, idx);
387*1b481fc3SMaciej Żenczykowski }
388*1b481fc3SMaciej Żenczykowski }
389*1b481fc3SMaciej Żenczykowski if (ret < 0)
390*1b481fc3SMaciej Żenczykowski goto err;
391*1b481fc3SMaciej Żenczykowski
392*1b481fc3SMaciej Żenczykowski nlctx->argp += 2;
393*1b481fc3SMaciej Żenczykowski nlctx->argc -= 2;
394*1b481fc3SMaciej Żenczykowski }
395*1b481fc3SMaciej Żenczykowski
396*1b481fc3SMaciej Żenczykowski ethnla_nest_end(msgbuff, bits_attr);
397*1b481fc3SMaciej Żenczykowski ethnla_nest_end(msgbuff, bitset_attr);
398*1b481fc3SMaciej Żenczykowski return 0;
399*1b481fc3SMaciej Żenczykowski err:
400*1b481fc3SMaciej Żenczykowski ethnla_nest_cancel(msgbuff, bitset_attr);
401*1b481fc3SMaciej Żenczykowski return ret;
402*1b481fc3SMaciej Żenczykowski }
403*1b481fc3SMaciej Żenczykowski
show_feature_changes(struct nl_context * nlctx,const struct nlattr * const * tb)404*1b481fc3SMaciej Żenczykowski static void show_feature_changes(struct nl_context *nlctx,
405*1b481fc3SMaciej Żenczykowski const struct nlattr *const *tb)
406*1b481fc3SMaciej Żenczykowski {
407*1b481fc3SMaciej Żenczykowski struct sfeatures_context *sfctx = nlctx->cmd_private;
408*1b481fc3SMaciej Żenczykowski const struct stringset *feature_names;
409*1b481fc3SMaciej Żenczykowski const uint32_t *wanted_mask;
410*1b481fc3SMaciej Żenczykowski const uint32_t *active_mask;
411*1b481fc3SMaciej Żenczykowski const uint32_t *wanted_val;
412*1b481fc3SMaciej Żenczykowski const uint32_t *active_val;
413*1b481fc3SMaciej Żenczykowski unsigned int count, words;
414*1b481fc3SMaciej Żenczykowski unsigned int i;
415*1b481fc3SMaciej Żenczykowski bool diff;
416*1b481fc3SMaciej Żenczykowski int ret;
417*1b481fc3SMaciej Żenczykowski
418*1b481fc3SMaciej Żenczykowski feature_names = global_stringset(ETH_SS_FEATURES, nlctx->ethnl_socket);
419*1b481fc3SMaciej Żenczykowski count = get_count(feature_names);
420*1b481fc3SMaciej Żenczykowski words = DIV_ROUND_UP(count, 32);
421*1b481fc3SMaciej Żenczykowski
422*1b481fc3SMaciej Żenczykowski if (!tb[ETHTOOL_A_FEATURES_WANTED] || !tb[ETHTOOL_A_FEATURES_ACTIVE])
423*1b481fc3SMaciej Żenczykowski goto err;
424*1b481fc3SMaciej Żenczykowski if (bitset_get_count(tb[ETHTOOL_A_FEATURES_WANTED], &ret) != count ||
425*1b481fc3SMaciej Żenczykowski ret < 0)
426*1b481fc3SMaciej Żenczykowski goto err;
427*1b481fc3SMaciej Żenczykowski if (bitset_get_count(tb[ETHTOOL_A_FEATURES_ACTIVE], &ret) != count ||
428*1b481fc3SMaciej Żenczykowski ret < 0)
429*1b481fc3SMaciej Żenczykowski goto err;
430*1b481fc3SMaciej Żenczykowski wanted_val = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_WANTED]);
431*1b481fc3SMaciej Żenczykowski wanted_mask = get_compact_bitset_mask(tb[ETHTOOL_A_FEATURES_WANTED]);
432*1b481fc3SMaciej Żenczykowski active_val = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_ACTIVE]);
433*1b481fc3SMaciej Żenczykowski active_mask = get_compact_bitset_mask(tb[ETHTOOL_A_FEATURES_ACTIVE]);
434*1b481fc3SMaciej Żenczykowski if (!wanted_val || !wanted_mask || !active_val || !active_mask)
435*1b481fc3SMaciej Żenczykowski goto err;
436*1b481fc3SMaciej Żenczykowski
437*1b481fc3SMaciej Żenczykowski sfctx->nothing_changed = true;
438*1b481fc3SMaciej Żenczykowski diff = false;
439*1b481fc3SMaciej Żenczykowski for (i = 0; i < words; i++) {
440*1b481fc3SMaciej Żenczykowski if (wanted_mask[i] != sfctx->req_mask[i])
441*1b481fc3SMaciej Żenczykowski sfctx->nothing_changed = false;
442*1b481fc3SMaciej Żenczykowski if (wanted_mask[i] || (active_mask[i] & ~sfctx->req_mask[i]))
443*1b481fc3SMaciej Żenczykowski diff = true;
444*1b481fc3SMaciej Żenczykowski }
445*1b481fc3SMaciej Żenczykowski if (!diff)
446*1b481fc3SMaciej Żenczykowski return;
447*1b481fc3SMaciej Żenczykowski
448*1b481fc3SMaciej Żenczykowski /* result is not exactly as requested, show differences */
449*1b481fc3SMaciej Żenczykowski printf("Actual changes:\n");
450*1b481fc3SMaciej Żenczykowski for (i = 0; i < count; i++) {
451*1b481fc3SMaciej Żenczykowski const char *name = get_string(feature_names, i);
452*1b481fc3SMaciej Żenczykowski
453*1b481fc3SMaciej Żenczykowski if (!name)
454*1b481fc3SMaciej Żenczykowski continue;
455*1b481fc3SMaciej Żenczykowski if (!feature_on(wanted_mask, i) && !feature_on(active_mask, i))
456*1b481fc3SMaciej Żenczykowski continue;
457*1b481fc3SMaciej Żenczykowski printf("%s: ", name);
458*1b481fc3SMaciej Żenczykowski if (feature_on(wanted_mask, i))
459*1b481fc3SMaciej Żenczykowski /* we requested a value but result is different */
460*1b481fc3SMaciej Żenczykowski printf("%s [requested %s]",
461*1b481fc3SMaciej Żenczykowski feature_on(wanted_val, i) ? "off" : "on",
462*1b481fc3SMaciej Żenczykowski feature_on(wanted_val, i) ? "on" : "off");
463*1b481fc3SMaciej Żenczykowski else if (!feature_on(sfctx->req_mask, i))
464*1b481fc3SMaciej Żenczykowski /* not requested but changed anyway */
465*1b481fc3SMaciej Żenczykowski printf("%s [not requested]",
466*1b481fc3SMaciej Żenczykowski feature_on(active_val, i) ? "on" : "off");
467*1b481fc3SMaciej Żenczykowski else
468*1b481fc3SMaciej Żenczykowski printf("%s", feature_on(active_val, i) ? "on" : "off");
469*1b481fc3SMaciej Żenczykowski fputc('\n', stdout);
470*1b481fc3SMaciej Żenczykowski }
471*1b481fc3SMaciej Żenczykowski
472*1b481fc3SMaciej Żenczykowski return;
473*1b481fc3SMaciej Żenczykowski err:
474*1b481fc3SMaciej Żenczykowski fprintf(stderr, "malformed diff info from kernel\n");
475*1b481fc3SMaciej Żenczykowski }
476*1b481fc3SMaciej Żenczykowski
sfeatures_reply_cb(const struct nlmsghdr * nlhdr,void * data)477*1b481fc3SMaciej Żenczykowski int sfeatures_reply_cb(const struct nlmsghdr *nlhdr, void *data)
478*1b481fc3SMaciej Żenczykowski {
479*1b481fc3SMaciej Żenczykowski const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
480*1b481fc3SMaciej Żenczykowski const struct nlattr *tb[ETHTOOL_A_FEATURES_MAX + 1] = {};
481*1b481fc3SMaciej Żenczykowski DECLARE_ATTR_TB_INFO(tb);
482*1b481fc3SMaciej Żenczykowski struct nl_context *nlctx = data;
483*1b481fc3SMaciej Żenczykowski const char *devname;
484*1b481fc3SMaciej Żenczykowski int ret;
485*1b481fc3SMaciej Żenczykowski
486*1b481fc3SMaciej Żenczykowski if (ghdr->cmd != ETHTOOL_MSG_FEATURES_SET_REPLY) {
487*1b481fc3SMaciej Żenczykowski fprintf(stderr, "warning: unexpected reply message type %u\n",
488*1b481fc3SMaciej Żenczykowski ghdr->cmd);
489*1b481fc3SMaciej Żenczykowski return MNL_CB_OK;
490*1b481fc3SMaciej Żenczykowski }
491*1b481fc3SMaciej Żenczykowski ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
492*1b481fc3SMaciej Żenczykowski if (ret < 0)
493*1b481fc3SMaciej Żenczykowski return ret;
494*1b481fc3SMaciej Żenczykowski devname = get_dev_name(tb[ETHTOOL_A_FEATURES_HEADER]);
495*1b481fc3SMaciej Żenczykowski if (strcmp(devname, nlctx->devname)) {
496*1b481fc3SMaciej Żenczykowski fprintf(stderr, "warning: unexpected message for device %s\n",
497*1b481fc3SMaciej Żenczykowski devname);
498*1b481fc3SMaciej Żenczykowski return MNL_CB_OK;
499*1b481fc3SMaciej Żenczykowski }
500*1b481fc3SMaciej Żenczykowski
501*1b481fc3SMaciej Żenczykowski show_feature_changes(nlctx, tb);
502*1b481fc3SMaciej Żenczykowski return MNL_CB_OK;
503*1b481fc3SMaciej Żenczykowski }
504*1b481fc3SMaciej Żenczykowski
nl_sfeatures(struct cmd_context * ctx)505*1b481fc3SMaciej Żenczykowski int nl_sfeatures(struct cmd_context *ctx)
506*1b481fc3SMaciej Żenczykowski {
507*1b481fc3SMaciej Żenczykowski const struct stringset *feature_names;
508*1b481fc3SMaciej Żenczykowski struct nl_context *nlctx = ctx->nlctx;
509*1b481fc3SMaciej Żenczykowski struct sfeatures_context *sfctx;
510*1b481fc3SMaciej Żenczykowski struct nl_msg_buff *msgbuff;
511*1b481fc3SMaciej Żenczykowski struct nl_socket *nlsk;
512*1b481fc3SMaciej Żenczykowski unsigned int words;
513*1b481fc3SMaciej Żenczykowski int ret;
514*1b481fc3SMaciej Żenczykowski
515*1b481fc3SMaciej Żenczykowski if (netlink_cmd_check(ctx, ETHTOOL_MSG_FEATURES_SET, false))
516*1b481fc3SMaciej Żenczykowski return -EOPNOTSUPP;
517*1b481fc3SMaciej Żenczykowski
518*1b481fc3SMaciej Żenczykowski nlctx->cmd = "-K";
519*1b481fc3SMaciej Żenczykowski nlctx->argp = ctx->argp;
520*1b481fc3SMaciej Żenczykowski nlctx->argc = ctx->argc;
521*1b481fc3SMaciej Żenczykowski nlctx->cmd_private = &sfctx;
522*1b481fc3SMaciej Żenczykowski nlsk = nlctx->ethnl_socket;
523*1b481fc3SMaciej Żenczykowski msgbuff = &nlsk->msgbuff;
524*1b481fc3SMaciej Żenczykowski
525*1b481fc3SMaciej Żenczykowski feature_names = global_stringset(ETH_SS_FEATURES, nlctx->ethnl_socket);
526*1b481fc3SMaciej Żenczykowski words = (get_count(feature_names) + 31) / 32;
527*1b481fc3SMaciej Żenczykowski sfctx = malloc(sizeof(*sfctx) + words * sizeof(sfctx->req_mask[0]));
528*1b481fc3SMaciej Żenczykowski if (!sfctx)
529*1b481fc3SMaciej Żenczykowski return -ENOMEM;
530*1b481fc3SMaciej Żenczykowski memset(sfctx, '\0',
531*1b481fc3SMaciej Żenczykowski sizeof(*sfctx) + words * sizeof(sfctx->req_mask[0]));
532*1b481fc3SMaciej Żenczykowski nlctx->cmd_private = sfctx;
533*1b481fc3SMaciej Żenczykowski
534*1b481fc3SMaciej Żenczykowski nlctx->devname = ctx->devname;
535*1b481fc3SMaciej Żenczykowski ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_FEATURES_SET,
536*1b481fc3SMaciej Żenczykowski NLM_F_REQUEST | NLM_F_ACK);
537*1b481fc3SMaciej Żenczykowski if (ret < 0) {
538*1b481fc3SMaciej Żenczykowski free(sfctx);
539*1b481fc3SMaciej Żenczykowski return 2;
540*1b481fc3SMaciej Żenczykowski }
541*1b481fc3SMaciej Żenczykowski if (ethnla_fill_header(msgbuff, ETHTOOL_A_FEATURES_HEADER, ctx->devname,
542*1b481fc3SMaciej Żenczykowski ETHTOOL_FLAG_COMPACT_BITSETS)) {
543*1b481fc3SMaciej Żenczykowski free(sfctx);
544*1b481fc3SMaciej Żenczykowski return -EMSGSIZE;
545*1b481fc3SMaciej Żenczykowski }
546*1b481fc3SMaciej Żenczykowski ret = fill_sfeatures_bitmap(nlctx, feature_names);
547*1b481fc3SMaciej Żenczykowski if (ret < 0) {
548*1b481fc3SMaciej Żenczykowski free(sfctx);
549*1b481fc3SMaciej Żenczykowski return ret;
550*1b481fc3SMaciej Żenczykowski }
551*1b481fc3SMaciej Żenczykowski
552*1b481fc3SMaciej Żenczykowski ret = nlsock_sendmsg(nlsk, NULL);
553*1b481fc3SMaciej Żenczykowski if (ret < 0) {
554*1b481fc3SMaciej Żenczykowski free(sfctx);
555*1b481fc3SMaciej Żenczykowski return 92;
556*1b481fc3SMaciej Żenczykowski }
557*1b481fc3SMaciej Żenczykowski ret = nlsock_process_reply(nlsk, sfeatures_reply_cb, nlctx);
558*1b481fc3SMaciej Żenczykowski if (sfctx->nothing_changed) {
559*1b481fc3SMaciej Żenczykowski fprintf(stderr, "Could not change any device features\n");
560*1b481fc3SMaciej Żenczykowski free(sfctx);
561*1b481fc3SMaciej Żenczykowski return nlctx->exit_code ?: 1;
562*1b481fc3SMaciej Żenczykowski }
563*1b481fc3SMaciej Żenczykowski if (ret == 0) {
564*1b481fc3SMaciej Żenczykowski free(sfctx);
565*1b481fc3SMaciej Żenczykowski return 0;
566*1b481fc3SMaciej Żenczykowski }
567*1b481fc3SMaciej Żenczykowski free(sfctx);
568*1b481fc3SMaciej Żenczykowski return nlctx->exit_code ?: 92;
569*1b481fc3SMaciej Żenczykowski }
570