Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2
3#include "netlink.h"
4#include "common.h"
5#include "bitset.h"
6
7struct privflags_req_info {
8 struct ethnl_req_info base;
9};
10
11struct privflags_reply_data {
12 struct ethnl_reply_data base;
13 const char (*priv_flag_names)[ETH_GSTRING_LEN];
14 unsigned int n_priv_flags;
15 u32 priv_flags;
16};
17
18#define PRIVFLAGS_REPDATA(__reply_base) \
19 container_of(__reply_base, struct privflags_reply_data, base)
20
21const struct nla_policy ethnl_privflags_get_policy[] = {
22 [ETHTOOL_A_PRIVFLAGS_HEADER] =
23 NLA_POLICY_NESTED(ethnl_header_policy),
24};
25
26static int ethnl_get_priv_flags_info(struct net_device *dev,
27 unsigned int *count,
28 const char (**names)[ETH_GSTRING_LEN])
29{
30 const struct ethtool_ops *ops = dev->ethtool_ops;
31 int nflags;
32
33 nflags = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
34 if (nflags < 0)
35 return nflags;
36
37 if (names) {
38 *names = kcalloc(nflags, ETH_GSTRING_LEN, GFP_KERNEL);
39 if (!*names)
40 return -ENOMEM;
41 ops->get_strings(dev, ETH_SS_PRIV_FLAGS, (u8 *)*names);
42 }
43
44 /* We can pass more than 32 private flags to userspace via netlink but
45 * we cannot get more with ethtool_ops::get_priv_flags(). Note that we
46 * must not adjust nflags before allocating the space for flag names
47 * as the buffer must be large enough for all flags.
48 */
49 if (WARN_ONCE(nflags > 32,
50 "device %s reports more than 32 private flags (%d)\n",
51 netdev_name(dev), nflags))
52 nflags = 32;
53 *count = nflags;
54
55 return 0;
56}
57
58static int privflags_prepare_data(const struct ethnl_req_info *req_base,
59 struct ethnl_reply_data *reply_base,
60 const struct genl_info *info)
61{
62 struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base);
63 struct net_device *dev = reply_base->dev;
64 const char (*names)[ETH_GSTRING_LEN];
65 const struct ethtool_ops *ops;
66 unsigned int nflags;
67 int ret;
68
69 ops = dev->ethtool_ops;
70 if (!ops->get_priv_flags || !ops->get_sset_count || !ops->get_strings)
71 return -EOPNOTSUPP;
72 ret = ethnl_ops_begin(dev);
73 if (ret < 0)
74 return ret;
75
76 ret = ethnl_get_priv_flags_info(dev, &nflags, &names);
77 if (ret < 0)
78 goto out_ops;
79 data->priv_flags = ops->get_priv_flags(dev);
80 data->priv_flag_names = names;
81 data->n_priv_flags = nflags;
82
83out_ops:
84 ethnl_ops_complete(dev);
85 return ret;
86}
87
88static int privflags_reply_size(const struct ethnl_req_info *req_base,
89 const struct ethnl_reply_data *reply_base)
90{
91 const struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base);
92 bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
93 const u32 all_flags = ~(u32)0 >> (32 - data->n_priv_flags);
94
95 return ethnl_bitset32_size(&data->priv_flags, &all_flags,
96 data->n_priv_flags,
97 data->priv_flag_names, compact);
98}
99
100static int privflags_fill_reply(struct sk_buff *skb,
101 const struct ethnl_req_info *req_base,
102 const struct ethnl_reply_data *reply_base)
103{
104 const struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base);
105 bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
106 const u32 all_flags = ~(u32)0 >> (32 - data->n_priv_flags);
107
108 return ethnl_put_bitset32(skb, ETHTOOL_A_PRIVFLAGS_FLAGS,
109 &data->priv_flags, &all_flags,
110 data->n_priv_flags, data->priv_flag_names,
111 compact);
112}
113
114static void privflags_cleanup_data(struct ethnl_reply_data *reply_data)
115{
116 struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_data);
117
118 kfree(data->priv_flag_names);
119}
120
121/* PRIVFLAGS_SET */
122
123const struct nla_policy ethnl_privflags_set_policy[] = {
124 [ETHTOOL_A_PRIVFLAGS_HEADER] =
125 NLA_POLICY_NESTED(ethnl_header_policy),
126 [ETHTOOL_A_PRIVFLAGS_FLAGS] = { .type = NLA_NESTED },
127};
128
129static int
130ethnl_set_privflags_validate(struct ethnl_req_info *req_info,
131 struct genl_info *info)
132{
133 const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
134
135 if (!info->attrs[ETHTOOL_A_PRIVFLAGS_FLAGS])
136 return -EINVAL;
137
138 if (!ops->get_priv_flags || !ops->set_priv_flags ||
139 !ops->get_sset_count || !ops->get_strings)
140 return -EOPNOTSUPP;
141 return 1;
142}
143
144static int
145ethnl_set_privflags(struct ethnl_req_info *req_info, struct genl_info *info)
146{
147 const char (*names)[ETH_GSTRING_LEN] = NULL;
148 struct net_device *dev = req_info->dev;
149 struct nlattr **tb = info->attrs;
150 unsigned int nflags;
151 bool mod = false;
152 bool compact;
153 u32 flags;
154 int ret;
155
156 ret = ethnl_bitset_is_compact(tb[ETHTOOL_A_PRIVFLAGS_FLAGS], &compact);
157 if (ret < 0)
158 return ret;
159
160 ret = ethnl_get_priv_flags_info(dev, &nflags, compact ? NULL : &names);
161 if (ret < 0)
162 return ret;
163 flags = dev->ethtool_ops->get_priv_flags(dev);
164
165 ret = ethnl_update_bitset32(&flags, nflags,
166 tb[ETHTOOL_A_PRIVFLAGS_FLAGS], names,
167 info->extack, &mod);
168 if (ret < 0 || !mod)
169 goto out_free;
170 ret = dev->ethtool_ops->set_priv_flags(dev, flags);
171 if (ret < 0)
172 goto out_free;
173 ret = 1;
174
175out_free:
176 kfree(names);
177 return ret;
178}
179
180const struct ethnl_request_ops ethnl_privflags_request_ops = {
181 .request_cmd = ETHTOOL_MSG_PRIVFLAGS_GET,
182 .reply_cmd = ETHTOOL_MSG_PRIVFLAGS_GET_REPLY,
183 .hdr_attr = ETHTOOL_A_PRIVFLAGS_HEADER,
184 .req_info_size = sizeof(struct privflags_req_info),
185 .reply_data_size = sizeof(struct privflags_reply_data),
186
187 .prepare_data = privflags_prepare_data,
188 .reply_size = privflags_reply_size,
189 .fill_reply = privflags_fill_reply,
190 .cleanup_data = privflags_cleanup_data,
191
192 .set_validate = ethnl_set_privflags_validate,
193 .set = ethnl_set_privflags,
194 .set_ntf_cmd = ETHTOOL_MSG_PRIVFLAGS_NTF,
195};
1// SPDX-License-Identifier: GPL-2.0-only
2
3#include "netlink.h"
4#include "common.h"
5#include "bitset.h"
6
7struct privflags_req_info {
8 struct ethnl_req_info base;
9};
10
11struct privflags_reply_data {
12 struct ethnl_reply_data base;
13 const char (*priv_flag_names)[ETH_GSTRING_LEN];
14 unsigned int n_priv_flags;
15 u32 priv_flags;
16};
17
18#define PRIVFLAGS_REPDATA(__reply_base) \
19 container_of(__reply_base, struct privflags_reply_data, base)
20
21static const struct nla_policy
22privflags_get_policy[ETHTOOL_A_PRIVFLAGS_MAX + 1] = {
23 [ETHTOOL_A_PRIVFLAGS_UNSPEC] = { .type = NLA_REJECT },
24 [ETHTOOL_A_PRIVFLAGS_HEADER] = { .type = NLA_NESTED },
25 [ETHTOOL_A_PRIVFLAGS_FLAGS] = { .type = NLA_REJECT },
26};
27
28static int ethnl_get_priv_flags_info(struct net_device *dev,
29 unsigned int *count,
30 const char (**names)[ETH_GSTRING_LEN])
31{
32 const struct ethtool_ops *ops = dev->ethtool_ops;
33 int nflags;
34
35 nflags = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
36 if (nflags < 0)
37 return nflags;
38
39 if (names) {
40 *names = kcalloc(nflags, ETH_GSTRING_LEN, GFP_KERNEL);
41 if (!*names)
42 return -ENOMEM;
43 ops->get_strings(dev, ETH_SS_PRIV_FLAGS, (u8 *)*names);
44 }
45
46 /* We can pass more than 32 private flags to userspace via netlink but
47 * we cannot get more with ethtool_ops::get_priv_flags(). Note that we
48 * must not adjust nflags before allocating the space for flag names
49 * as the buffer must be large enough for all flags.
50 */
51 if (WARN_ONCE(nflags > 32,
52 "device %s reports more than 32 private flags (%d)\n",
53 netdev_name(dev), nflags))
54 nflags = 32;
55 *count = nflags;
56
57 return 0;
58}
59
60static int privflags_prepare_data(const struct ethnl_req_info *req_base,
61 struct ethnl_reply_data *reply_base,
62 struct genl_info *info)
63{
64 struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base);
65 struct net_device *dev = reply_base->dev;
66 const char (*names)[ETH_GSTRING_LEN];
67 const struct ethtool_ops *ops;
68 unsigned int nflags;
69 int ret;
70
71 ops = dev->ethtool_ops;
72 if (!ops->get_priv_flags || !ops->get_sset_count || !ops->get_strings)
73 return -EOPNOTSUPP;
74 ret = ethnl_ops_begin(dev);
75 if (ret < 0)
76 return ret;
77
78 ret = ethnl_get_priv_flags_info(dev, &nflags, &names);
79 if (ret < 0)
80 goto out_ops;
81 data->priv_flags = ops->get_priv_flags(dev);
82 data->priv_flag_names = names;
83 data->n_priv_flags = nflags;
84
85out_ops:
86 ethnl_ops_complete(dev);
87 return ret;
88}
89
90static int privflags_reply_size(const struct ethnl_req_info *req_base,
91 const struct ethnl_reply_data *reply_base)
92{
93 const struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base);
94 bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
95 const u32 all_flags = ~(u32)0 >> (32 - data->n_priv_flags);
96
97 return ethnl_bitset32_size(&data->priv_flags, &all_flags,
98 data->n_priv_flags,
99 data->priv_flag_names, compact);
100}
101
102static int privflags_fill_reply(struct sk_buff *skb,
103 const struct ethnl_req_info *req_base,
104 const struct ethnl_reply_data *reply_base)
105{
106 const struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base);
107 bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
108 const u32 all_flags = ~(u32)0 >> (32 - data->n_priv_flags);
109
110 return ethnl_put_bitset32(skb, ETHTOOL_A_PRIVFLAGS_FLAGS,
111 &data->priv_flags, &all_flags,
112 data->n_priv_flags, data->priv_flag_names,
113 compact);
114}
115
116static void privflags_cleanup_data(struct ethnl_reply_data *reply_data)
117{
118 struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_data);
119
120 kfree(data->priv_flag_names);
121}
122
123const struct ethnl_request_ops ethnl_privflags_request_ops = {
124 .request_cmd = ETHTOOL_MSG_PRIVFLAGS_GET,
125 .reply_cmd = ETHTOOL_MSG_PRIVFLAGS_GET_REPLY,
126 .hdr_attr = ETHTOOL_A_PRIVFLAGS_HEADER,
127 .max_attr = ETHTOOL_A_PRIVFLAGS_MAX,
128 .req_info_size = sizeof(struct privflags_req_info),
129 .reply_data_size = sizeof(struct privflags_reply_data),
130 .request_policy = privflags_get_policy,
131
132 .prepare_data = privflags_prepare_data,
133 .reply_size = privflags_reply_size,
134 .fill_reply = privflags_fill_reply,
135 .cleanup_data = privflags_cleanup_data,
136};
137
138/* PRIVFLAGS_SET */
139
140static const struct nla_policy
141privflags_set_policy[ETHTOOL_A_PRIVFLAGS_MAX + 1] = {
142 [ETHTOOL_A_PRIVFLAGS_UNSPEC] = { .type = NLA_REJECT },
143 [ETHTOOL_A_PRIVFLAGS_HEADER] = { .type = NLA_NESTED },
144 [ETHTOOL_A_PRIVFLAGS_FLAGS] = { .type = NLA_NESTED },
145};
146
147int ethnl_set_privflags(struct sk_buff *skb, struct genl_info *info)
148{
149 struct nlattr *tb[ETHTOOL_A_PRIVFLAGS_MAX + 1];
150 const char (*names)[ETH_GSTRING_LEN] = NULL;
151 struct ethnl_req_info req_info = {};
152 const struct ethtool_ops *ops;
153 struct net_device *dev;
154 unsigned int nflags;
155 bool mod = false;
156 bool compact;
157 u32 flags;
158 int ret;
159
160 ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb,
161 ETHTOOL_A_PRIVFLAGS_MAX, privflags_set_policy,
162 info->extack);
163 if (ret < 0)
164 return ret;
165 if (!tb[ETHTOOL_A_PRIVFLAGS_FLAGS])
166 return -EINVAL;
167 ret = ethnl_bitset_is_compact(tb[ETHTOOL_A_PRIVFLAGS_FLAGS], &compact);
168 if (ret < 0)
169 return ret;
170 ret = ethnl_parse_header_dev_get(&req_info,
171 tb[ETHTOOL_A_PRIVFLAGS_HEADER],
172 genl_info_net(info), info->extack,
173 true);
174 if (ret < 0)
175 return ret;
176 dev = req_info.dev;
177 ops = dev->ethtool_ops;
178 ret = -EOPNOTSUPP;
179 if (!ops->get_priv_flags || !ops->set_priv_flags ||
180 !ops->get_sset_count || !ops->get_strings)
181 goto out_dev;
182
183 rtnl_lock();
184 ret = ethnl_ops_begin(dev);
185 if (ret < 0)
186 goto out_rtnl;
187 ret = ethnl_get_priv_flags_info(dev, &nflags, compact ? NULL : &names);
188 if (ret < 0)
189 goto out_ops;
190 flags = ops->get_priv_flags(dev);
191
192 ret = ethnl_update_bitset32(&flags, nflags,
193 tb[ETHTOOL_A_PRIVFLAGS_FLAGS], names,
194 info->extack, &mod);
195 if (ret < 0 || !mod)
196 goto out_free;
197 ret = ops->set_priv_flags(dev, flags);
198 if (ret < 0)
199 goto out_free;
200 ethtool_notify(dev, ETHTOOL_MSG_PRIVFLAGS_NTF, NULL);
201
202out_free:
203 kfree(names);
204out_ops:
205 ethnl_ops_complete(dev);
206out_rtnl:
207 rtnl_unlock();
208out_dev:
209 dev_put(dev);
210 return ret;
211}