1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun
3*4882a593Smuzhiyun #include <linux/ethtool_netlink.h>
4*4882a593Smuzhiyun #include <net/udp_tunnel.h>
5*4882a593Smuzhiyun #include <net/vxlan.h>
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include "bitset.h"
8*4882a593Smuzhiyun #include "common.h"
9*4882a593Smuzhiyun #include "netlink.h"
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun const struct nla_policy ethnl_tunnel_info_get_policy[] = {
12*4882a593Smuzhiyun [ETHTOOL_A_TUNNEL_INFO_HEADER] =
13*4882a593Smuzhiyun NLA_POLICY_NESTED(ethnl_header_policy),
14*4882a593Smuzhiyun };
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun static_assert(ETHTOOL_UDP_TUNNEL_TYPE_VXLAN == ilog2(UDP_TUNNEL_TYPE_VXLAN));
17*4882a593Smuzhiyun static_assert(ETHTOOL_UDP_TUNNEL_TYPE_GENEVE == ilog2(UDP_TUNNEL_TYPE_GENEVE));
18*4882a593Smuzhiyun static_assert(ETHTOOL_UDP_TUNNEL_TYPE_VXLAN_GPE ==
19*4882a593Smuzhiyun ilog2(UDP_TUNNEL_TYPE_VXLAN_GPE));
20*4882a593Smuzhiyun
ethnl_udp_table_reply_size(unsigned int types,bool compact)21*4882a593Smuzhiyun static ssize_t ethnl_udp_table_reply_size(unsigned int types, bool compact)
22*4882a593Smuzhiyun {
23*4882a593Smuzhiyun ssize_t size;
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun size = ethnl_bitset32_size(&types, NULL, __ETHTOOL_UDP_TUNNEL_TYPE_CNT,
26*4882a593Smuzhiyun udp_tunnel_type_names, compact);
27*4882a593Smuzhiyun if (size < 0)
28*4882a593Smuzhiyun return size;
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun return size +
31*4882a593Smuzhiyun nla_total_size(0) + /* _UDP_TABLE */
32*4882a593Smuzhiyun nla_total_size(sizeof(u32)); /* _UDP_TABLE_SIZE */
33*4882a593Smuzhiyun }
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun static ssize_t
ethnl_tunnel_info_reply_size(const struct ethnl_req_info * req_base,struct netlink_ext_ack * extack)36*4882a593Smuzhiyun ethnl_tunnel_info_reply_size(const struct ethnl_req_info *req_base,
37*4882a593Smuzhiyun struct netlink_ext_ack *extack)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
40*4882a593Smuzhiyun const struct udp_tunnel_nic_info *info;
41*4882a593Smuzhiyun unsigned int i;
42*4882a593Smuzhiyun ssize_t ret;
43*4882a593Smuzhiyun size_t size;
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun info = req_base->dev->udp_tunnel_nic_info;
46*4882a593Smuzhiyun if (!info) {
47*4882a593Smuzhiyun NL_SET_ERR_MSG(extack,
48*4882a593Smuzhiyun "device does not report tunnel offload info");
49*4882a593Smuzhiyun return -EOPNOTSUPP;
50*4882a593Smuzhiyun }
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun size = nla_total_size(0); /* _INFO_UDP_PORTS */
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun for (i = 0; i < UDP_TUNNEL_NIC_MAX_TABLES; i++) {
55*4882a593Smuzhiyun if (!info->tables[i].n_entries)
56*4882a593Smuzhiyun break;
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun ret = ethnl_udp_table_reply_size(info->tables[i].tunnel_types,
59*4882a593Smuzhiyun compact);
60*4882a593Smuzhiyun if (ret < 0)
61*4882a593Smuzhiyun return ret;
62*4882a593Smuzhiyun size += ret;
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun size += udp_tunnel_nic_dump_size(req_base->dev, i);
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun if (info->flags & UDP_TUNNEL_NIC_INFO_STATIC_IANA_VXLAN) {
68*4882a593Smuzhiyun ret = ethnl_udp_table_reply_size(0, compact);
69*4882a593Smuzhiyun if (ret < 0)
70*4882a593Smuzhiyun return ret;
71*4882a593Smuzhiyun size += ret;
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun size += nla_total_size(0) + /* _TABLE_ENTRY */
74*4882a593Smuzhiyun nla_total_size(sizeof(__be16)) + /* _ENTRY_PORT */
75*4882a593Smuzhiyun nla_total_size(sizeof(u32)); /* _ENTRY_TYPE */
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun return size;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun static int
ethnl_tunnel_info_fill_reply(const struct ethnl_req_info * req_base,struct sk_buff * skb)82*4882a593Smuzhiyun ethnl_tunnel_info_fill_reply(const struct ethnl_req_info *req_base,
83*4882a593Smuzhiyun struct sk_buff *skb)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
86*4882a593Smuzhiyun const struct udp_tunnel_nic_info *info;
87*4882a593Smuzhiyun struct nlattr *ports, *table, *entry;
88*4882a593Smuzhiyun unsigned int i;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun info = req_base->dev->udp_tunnel_nic_info;
91*4882a593Smuzhiyun if (!info)
92*4882a593Smuzhiyun return -EOPNOTSUPP;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun ports = nla_nest_start(skb, ETHTOOL_A_TUNNEL_INFO_UDP_PORTS);
95*4882a593Smuzhiyun if (!ports)
96*4882a593Smuzhiyun return -EMSGSIZE;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun for (i = 0; i < UDP_TUNNEL_NIC_MAX_TABLES; i++) {
99*4882a593Smuzhiyun if (!info->tables[i].n_entries)
100*4882a593Smuzhiyun break;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun table = nla_nest_start(skb, ETHTOOL_A_TUNNEL_UDP_TABLE);
103*4882a593Smuzhiyun if (!table)
104*4882a593Smuzhiyun goto err_cancel_ports;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun if (nla_put_u32(skb, ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE,
107*4882a593Smuzhiyun info->tables[i].n_entries))
108*4882a593Smuzhiyun goto err_cancel_table;
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun if (ethnl_put_bitset32(skb, ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES,
111*4882a593Smuzhiyun &info->tables[i].tunnel_types, NULL,
112*4882a593Smuzhiyun __ETHTOOL_UDP_TUNNEL_TYPE_CNT,
113*4882a593Smuzhiyun udp_tunnel_type_names, compact))
114*4882a593Smuzhiyun goto err_cancel_table;
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun if (udp_tunnel_nic_dump_write(req_base->dev, i, skb))
117*4882a593Smuzhiyun goto err_cancel_table;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun nla_nest_end(skb, table);
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun if (info->flags & UDP_TUNNEL_NIC_INFO_STATIC_IANA_VXLAN) {
123*4882a593Smuzhiyun u32 zero = 0;
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun table = nla_nest_start(skb, ETHTOOL_A_TUNNEL_UDP_TABLE);
126*4882a593Smuzhiyun if (!table)
127*4882a593Smuzhiyun goto err_cancel_ports;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun if (nla_put_u32(skb, ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE, 1))
130*4882a593Smuzhiyun goto err_cancel_table;
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun if (ethnl_put_bitset32(skb, ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES,
133*4882a593Smuzhiyun &zero, NULL,
134*4882a593Smuzhiyun __ETHTOOL_UDP_TUNNEL_TYPE_CNT,
135*4882a593Smuzhiyun udp_tunnel_type_names, compact))
136*4882a593Smuzhiyun goto err_cancel_table;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun entry = nla_nest_start(skb, ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY);
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun if (nla_put_be16(skb, ETHTOOL_A_TUNNEL_UDP_ENTRY_PORT,
141*4882a593Smuzhiyun htons(IANA_VXLAN_UDP_PORT)) ||
142*4882a593Smuzhiyun nla_put_u32(skb, ETHTOOL_A_TUNNEL_UDP_ENTRY_TYPE,
143*4882a593Smuzhiyun ilog2(UDP_TUNNEL_TYPE_VXLAN)))
144*4882a593Smuzhiyun goto err_cancel_entry;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun nla_nest_end(skb, entry);
147*4882a593Smuzhiyun nla_nest_end(skb, table);
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun nla_nest_end(skb, ports);
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun return 0;
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun err_cancel_entry:
155*4882a593Smuzhiyun nla_nest_cancel(skb, entry);
156*4882a593Smuzhiyun err_cancel_table:
157*4882a593Smuzhiyun nla_nest_cancel(skb, table);
158*4882a593Smuzhiyun err_cancel_ports:
159*4882a593Smuzhiyun nla_nest_cancel(skb, ports);
160*4882a593Smuzhiyun return -EMSGSIZE;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
ethnl_tunnel_info_doit(struct sk_buff * skb,struct genl_info * info)163*4882a593Smuzhiyun int ethnl_tunnel_info_doit(struct sk_buff *skb, struct genl_info *info)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun struct ethnl_req_info req_info = {};
166*4882a593Smuzhiyun struct nlattr **tb = info->attrs;
167*4882a593Smuzhiyun struct sk_buff *rskb;
168*4882a593Smuzhiyun void *reply_payload;
169*4882a593Smuzhiyun int reply_len;
170*4882a593Smuzhiyun int ret;
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun ret = ethnl_parse_header_dev_get(&req_info,
173*4882a593Smuzhiyun tb[ETHTOOL_A_TUNNEL_INFO_HEADER],
174*4882a593Smuzhiyun genl_info_net(info), info->extack,
175*4882a593Smuzhiyun true);
176*4882a593Smuzhiyun if (ret < 0)
177*4882a593Smuzhiyun return ret;
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun rtnl_lock();
180*4882a593Smuzhiyun ret = ethnl_tunnel_info_reply_size(&req_info, info->extack);
181*4882a593Smuzhiyun if (ret < 0)
182*4882a593Smuzhiyun goto err_unlock_rtnl;
183*4882a593Smuzhiyun reply_len = ret + ethnl_reply_header_size();
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun rskb = ethnl_reply_init(reply_len, req_info.dev,
186*4882a593Smuzhiyun ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY,
187*4882a593Smuzhiyun ETHTOOL_A_TUNNEL_INFO_HEADER,
188*4882a593Smuzhiyun info, &reply_payload);
189*4882a593Smuzhiyun if (!rskb) {
190*4882a593Smuzhiyun ret = -ENOMEM;
191*4882a593Smuzhiyun goto err_unlock_rtnl;
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun ret = ethnl_tunnel_info_fill_reply(&req_info, rskb);
195*4882a593Smuzhiyun if (ret)
196*4882a593Smuzhiyun goto err_free_msg;
197*4882a593Smuzhiyun rtnl_unlock();
198*4882a593Smuzhiyun dev_put(req_info.dev);
199*4882a593Smuzhiyun genlmsg_end(rskb, reply_payload);
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun return genlmsg_reply(rskb, info);
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun err_free_msg:
204*4882a593Smuzhiyun nlmsg_free(rskb);
205*4882a593Smuzhiyun err_unlock_rtnl:
206*4882a593Smuzhiyun rtnl_unlock();
207*4882a593Smuzhiyun dev_put(req_info.dev);
208*4882a593Smuzhiyun return ret;
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun struct ethnl_tunnel_info_dump_ctx {
212*4882a593Smuzhiyun struct ethnl_req_info req_info;
213*4882a593Smuzhiyun int pos_hash;
214*4882a593Smuzhiyun int pos_idx;
215*4882a593Smuzhiyun };
216*4882a593Smuzhiyun
ethnl_tunnel_info_start(struct netlink_callback * cb)217*4882a593Smuzhiyun int ethnl_tunnel_info_start(struct netlink_callback *cb)
218*4882a593Smuzhiyun {
219*4882a593Smuzhiyun const struct genl_dumpit_info *info = genl_dumpit_info(cb);
220*4882a593Smuzhiyun struct ethnl_tunnel_info_dump_ctx *ctx = (void *)cb->ctx;
221*4882a593Smuzhiyun struct nlattr **tb = info->attrs;
222*4882a593Smuzhiyun int ret;
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx));
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun memset(ctx, 0, sizeof(*ctx));
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun ret = ethnl_parse_header_dev_get(&ctx->req_info,
229*4882a593Smuzhiyun tb[ETHTOOL_A_TUNNEL_INFO_HEADER],
230*4882a593Smuzhiyun sock_net(cb->skb->sk), cb->extack,
231*4882a593Smuzhiyun false);
232*4882a593Smuzhiyun if (ctx->req_info.dev) {
233*4882a593Smuzhiyun dev_put(ctx->req_info.dev);
234*4882a593Smuzhiyun ctx->req_info.dev = NULL;
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun return ret;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
ethnl_tunnel_info_dumpit(struct sk_buff * skb,struct netlink_callback * cb)240*4882a593Smuzhiyun int ethnl_tunnel_info_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun struct ethnl_tunnel_info_dump_ctx *ctx = (void *)cb->ctx;
243*4882a593Smuzhiyun struct net *net = sock_net(skb->sk);
244*4882a593Smuzhiyun int s_idx = ctx->pos_idx;
245*4882a593Smuzhiyun int h, idx = 0;
246*4882a593Smuzhiyun int ret = 0;
247*4882a593Smuzhiyun void *ehdr;
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun rtnl_lock();
250*4882a593Smuzhiyun cb->seq = net->dev_base_seq;
251*4882a593Smuzhiyun for (h = ctx->pos_hash; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
252*4882a593Smuzhiyun struct hlist_head *head;
253*4882a593Smuzhiyun struct net_device *dev;
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun head = &net->dev_index_head[h];
256*4882a593Smuzhiyun idx = 0;
257*4882a593Smuzhiyun hlist_for_each_entry(dev, head, index_hlist) {
258*4882a593Smuzhiyun if (idx < s_idx)
259*4882a593Smuzhiyun goto cont;
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun ehdr = ethnl_dump_put(skb, cb,
262*4882a593Smuzhiyun ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY);
263*4882a593Smuzhiyun if (!ehdr) {
264*4882a593Smuzhiyun ret = -EMSGSIZE;
265*4882a593Smuzhiyun goto out;
266*4882a593Smuzhiyun }
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun ret = ethnl_fill_reply_header(skb, dev, ETHTOOL_A_TUNNEL_INFO_HEADER);
269*4882a593Smuzhiyun if (ret < 0) {
270*4882a593Smuzhiyun genlmsg_cancel(skb, ehdr);
271*4882a593Smuzhiyun goto out;
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun ctx->req_info.dev = dev;
275*4882a593Smuzhiyun ret = ethnl_tunnel_info_fill_reply(&ctx->req_info, skb);
276*4882a593Smuzhiyun ctx->req_info.dev = NULL;
277*4882a593Smuzhiyun if (ret < 0) {
278*4882a593Smuzhiyun genlmsg_cancel(skb, ehdr);
279*4882a593Smuzhiyun if (ret == -EOPNOTSUPP)
280*4882a593Smuzhiyun goto cont;
281*4882a593Smuzhiyun goto out;
282*4882a593Smuzhiyun }
283*4882a593Smuzhiyun genlmsg_end(skb, ehdr);
284*4882a593Smuzhiyun cont:
285*4882a593Smuzhiyun idx++;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun out:
289*4882a593Smuzhiyun rtnl_unlock();
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun ctx->pos_hash = h;
292*4882a593Smuzhiyun ctx->pos_idx = idx;
293*4882a593Smuzhiyun nl_dump_check_consistent(cb, nlmsg_hdr(skb));
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun if (ret == -EMSGSIZE && skb->len)
296*4882a593Smuzhiyun return skb->len;
297*4882a593Smuzhiyun return ret;
298*4882a593Smuzhiyun }
299