xref: /OK3568_Linux_fs/kernel/net/ethtool/tunnels.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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