File Index
Files and directories
Sign in
FILE

act_vlan.c

File preview with syntax highlighting.

linux-6.19.14/net/sched/act_vlan.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
  4 */
  5
  6#include <linux/module.h>
  7#include <linux/init.h>
  8#include <linux/kernel.h>
  9#include <linux/skbuff.h>
 10#include <linux/rtnetlink.h>
 11#include <linux/if_vlan.h>
 12#include <net/netlink.h>
 13#include <net/pkt_sched.h>
 14#include <net/pkt_cls.h>
 15#include <net/tc_wrapper.h>
 16
 17#include <linux/tc_act/tc_vlan.h>
 18#include <net/tc_act/tc_vlan.h>
 19
 20static struct tc_action_ops act_vlan_ops;
 21
 22TC_INDIRECT_SCOPE int tcf_vlan_act(struct sk_buff *skb,
 23				   const struct tc_action *a,
 24				   struct tcf_result *res)
 25{
 26	struct tcf_vlan *v = to_vlan(a);
 27	struct tcf_vlan_params *p;
 28	int err;
 29	u16 tci;
 30
 31	tcf_lastuse_update(&v->tcf_tm);
 32	tcf_action_update_bstats(&v->common, skb);
 33
 34	/* Ensure 'data' points at mac_header prior calling vlan manipulating
 35	 * functions.
 36	 */
 37	if (skb_at_tc_ingress(skb))
 38		skb_push_rcsum(skb, skb->mac_len);
 39
 40	p = rcu_dereference_bh(v->vlan_p);
 41
 42	switch (p->tcfv_action) {
 43	case TCA_VLAN_ACT_POP:
 44		err = skb_vlan_pop(skb);
 45		if (err)
 46			goto drop;
 47		break;
 48	case TCA_VLAN_ACT_PUSH:
 49		err = skb_vlan_push(skb, p->tcfv_push_proto, p->tcfv_push_vid |
 50				    (p->tcfv_push_prio << VLAN_PRIO_SHIFT));
 51		if (err)
 52			goto drop;
 53		break;
 54	case TCA_VLAN_ACT_MODIFY:
 55		/* No-op if no vlan tag (either hw-accel or in-payload) */
 56		if (!skb_vlan_tagged(skb))
 57			goto out;
 58		/* extract existing tag (and guarantee no hw-accel tag) */
 59		if (skb_vlan_tag_present(skb)) {
 60			tci = skb_vlan_tag_get(skb);
 61			__vlan_hwaccel_clear_tag(skb);
 62		} else {
 63			/* in-payload vlan tag, pop it */
 64			err = __skb_vlan_pop(skb, &tci);
 65			if (err)
 66				goto drop;
 67		}
 68		/* replace the vid */
 69		tci = (tci & ~VLAN_VID_MASK) | p->tcfv_push_vid;
 70		/* replace prio bits, if tcfv_push_prio specified */
 71		if (p->tcfv_push_prio_exists) {
 72			tci &= ~VLAN_PRIO_MASK;
 73			tci |= p->tcfv_push_prio << VLAN_PRIO_SHIFT;
 74		}
 75		/* put updated tci as hwaccel tag */
 76		__vlan_hwaccel_put_tag(skb, p->tcfv_push_proto, tci);
 77		break;
 78	case TCA_VLAN_ACT_POP_ETH:
 79		err = skb_eth_pop(skb);
 80		if (err)
 81			goto drop;
 82		break;
 83	case TCA_VLAN_ACT_PUSH_ETH:
 84		err = skb_eth_push(skb, p->tcfv_push_dst, p->tcfv_push_src);
 85		if (err)
 86			goto drop;
 87		break;
 88	default:
 89		BUG();
 90	}
 91
 92out:
 93	if (skb_at_tc_ingress(skb))
 94		skb_pull_rcsum(skb, skb->mac_len);
 95
 96	skb_reset_mac_len(skb);
 97	return p->action;
 98
 99drop:
100	tcf_action_inc_drop_qstats(&v->common);
101	return TC_ACT_SHOT;
102}
103
104static const struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = {
105	[TCA_VLAN_UNSPEC]		= { .strict_start_type = TCA_VLAN_PUSH_ETH_DST },
106	[TCA_VLAN_PARMS]		= { .len = sizeof(struct tc_vlan) },
107	[TCA_VLAN_PUSH_VLAN_ID]		= { .type = NLA_U16 },
108	[TCA_VLAN_PUSH_VLAN_PROTOCOL]	= { .type = NLA_U16 },
109	[TCA_VLAN_PUSH_VLAN_PRIORITY]	= { .type = NLA_U8 },
110	[TCA_VLAN_PUSH_ETH_DST]		= NLA_POLICY_ETH_ADDR,
111	[TCA_VLAN_PUSH_ETH_SRC]		= NLA_POLICY_ETH_ADDR,
112};
113
114static int tcf_vlan_init(struct net *net, struct nlattr *nla,
115			 struct nlattr *est, struct tc_action **a,
116			 struct tcf_proto *tp, u32 flags,
117			 struct netlink_ext_ack *extack)
118{
119	struct tc_action_net *tn = net_generic(net, act_vlan_ops.net_id);
120	bool bind = flags & TCA_ACT_FLAGS_BIND;
121	struct nlattr *tb[TCA_VLAN_MAX + 1];
122	struct tcf_chain *goto_ch = NULL;
123	bool push_prio_exists = false;
124	struct tcf_vlan_params *p;
125	struct tc_vlan *parm;
126	struct tcf_vlan *v;
127	int action;
128	u16 push_vid = 0;
129	__be16 push_proto = 0;
130	u8 push_prio = 0;
131	bool exists = false;
132	int ret = 0, err;
133	u32 index;
134
135	if (!nla)
136		return -EINVAL;
137
138	err = nla_parse_nested_deprecated(tb, TCA_VLAN_MAX, nla, vlan_policy,
139					  NULL);
140	if (err < 0)
141		return err;
142
143	if (!tb[TCA_VLAN_PARMS])
144		return -EINVAL;
145	parm = nla_data(tb[TCA_VLAN_PARMS]);
146	index = parm->index;
147	err = tcf_idr_check_alloc(tn, &index, a, bind);
148	if (err < 0)
149		return err;
150	exists = err;
151	if (exists && bind)
152		return ACT_P_BOUND;
153
154	switch (parm->v_action) {
155	case TCA_VLAN_ACT_POP:
156		break;
157	case TCA_VLAN_ACT_PUSH:
158	case TCA_VLAN_ACT_MODIFY:
159		if (!tb[TCA_VLAN_PUSH_VLAN_ID]) {
160			if (exists)
161				tcf_idr_release(*a, bind);
162			else
163				tcf_idr_cleanup(tn, index);
164			return -EINVAL;
165		}
166		push_vid = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_ID]);
167		if (push_vid >= VLAN_VID_MASK) {
168			if (exists)
169				tcf_idr_release(*a, bind);
170			else
171				tcf_idr_cleanup(tn, index);
172			return -ERANGE;
173		}
174
175		if (tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]) {
176			push_proto = nla_get_be16(tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]);
177			switch (push_proto) {
178			case htons(ETH_P_8021Q):
179			case htons(ETH_P_8021AD):
180				break;
181			default:
182				if (exists)
183					tcf_idr_release(*a, bind);
184				else
185					tcf_idr_cleanup(tn, index);
186				return -EPROTONOSUPPORT;
187			}
188		} else {
189			push_proto = htons(ETH_P_8021Q);
190		}
191
192		push_prio_exists = !!tb[TCA_VLAN_PUSH_VLAN_PRIORITY];
193		if (push_prio_exists)
194			push_prio = nla_get_u8(tb[TCA_VLAN_PUSH_VLAN_PRIORITY]);
195		break;
196	case TCA_VLAN_ACT_POP_ETH:
197		break;
198	case TCA_VLAN_ACT_PUSH_ETH:
199		if (!tb[TCA_VLAN_PUSH_ETH_DST] || !tb[TCA_VLAN_PUSH_ETH_SRC]) {
200			if (exists)
201				tcf_idr_release(*a, bind);
202			else
203				tcf_idr_cleanup(tn, index);
204			return -EINVAL;
205		}
206		break;
207	default:
208		if (exists)
209			tcf_idr_release(*a, bind);
210		else
211			tcf_idr_cleanup(tn, index);
212		return -EINVAL;
213	}
214	action = parm->v_action;
215
216	if (!exists) {
217		ret = tcf_idr_create_from_flags(tn, index, est, a,
218						&act_vlan_ops, bind, flags);
219		if (ret) {
220			tcf_idr_cleanup(tn, index);
221			return ret;
222		}
223
224		ret = ACT_P_CREATED;
225	} else if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
226		tcf_idr_release(*a, bind);
227		return -EEXIST;
228	}
229
230	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
231	if (err < 0)
232		goto release_idr;
233
234	v = to_vlan(*a);
235
236	p = kzalloc(sizeof(*p), GFP_KERNEL);
237	if (!p) {
238		err = -ENOMEM;
239		goto put_chain;
240	}
241
242	p->tcfv_action = action;
243	p->tcfv_push_vid = push_vid;
244	p->tcfv_push_prio = push_prio;
245	p->tcfv_push_prio_exists = push_prio_exists || action == TCA_VLAN_ACT_PUSH;
246	p->tcfv_push_proto = push_proto;
247
248	if (action == TCA_VLAN_ACT_PUSH_ETH) {
249		nla_memcpy(&p->tcfv_push_dst, tb[TCA_VLAN_PUSH_ETH_DST],
250			   ETH_ALEN);
251		nla_memcpy(&p->tcfv_push_src, tb[TCA_VLAN_PUSH_ETH_SRC],
252			   ETH_ALEN);
253	}
254
255	p->action = parm->action;
256	spin_lock_bh(&v->tcf_lock);
257	goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
258	p = rcu_replace_pointer(v->vlan_p, p, lockdep_is_held(&v->tcf_lock));
259	spin_unlock_bh(&v->tcf_lock);
260
261	if (goto_ch)
262		tcf_chain_put_by_act(goto_ch);
263	if (p)
264		kfree_rcu(p, rcu);
265
266	return ret;
267put_chain:
268	if (goto_ch)
269		tcf_chain_put_by_act(goto_ch);
270release_idr:
271	tcf_idr_release(*a, bind);
272	return err;
273}
274
275static void tcf_vlan_cleanup(struct tc_action *a)
276{
277	struct tcf_vlan *v = to_vlan(a);
278	struct tcf_vlan_params *p;
279
280	p = rcu_dereference_protected(v->vlan_p, 1);
281	if (p)
282		kfree_rcu(p, rcu);
283}
284
285static int tcf_vlan_dump(struct sk_buff *skb, struct tc_action *a,
286			 int bind, int ref)
287{
288	unsigned char *b = skb_tail_pointer(skb);
289	struct tcf_vlan *v = to_vlan(a);
290	struct tcf_vlan_params *p;
291	struct tc_vlan opt = {
292		.index    = v->tcf_index,
293		.refcnt   = refcount_read(&v->tcf_refcnt) - ref,
294		.bindcnt  = atomic_read(&v->tcf_bindcnt) - bind,
295	};
296	struct tcf_t t;
297
298	rcu_read_lock();
299	p = rcu_dereference(v->vlan_p);
300	opt.action = p->action;
301	opt.v_action = p->tcfv_action;
302	if (nla_put(skb, TCA_VLAN_PARMS, sizeof(opt), &opt))
303		goto nla_put_failure;
304
305	if ((p->tcfv_action == TCA_VLAN_ACT_PUSH ||
306	     p->tcfv_action == TCA_VLAN_ACT_MODIFY) &&
307	    (nla_put_u16(skb, TCA_VLAN_PUSH_VLAN_ID, p->tcfv_push_vid) ||
308	     nla_put_be16(skb, TCA_VLAN_PUSH_VLAN_PROTOCOL,
309			  p->tcfv_push_proto) ||
310	     (p->tcfv_push_prio_exists &&
311	      nla_put_u8(skb, TCA_VLAN_PUSH_VLAN_PRIORITY, p->tcfv_push_prio))))
312		goto nla_put_failure;
313
314	if (p->tcfv_action == TCA_VLAN_ACT_PUSH_ETH) {
315		if (nla_put(skb, TCA_VLAN_PUSH_ETH_DST, ETH_ALEN,
316			    p->tcfv_push_dst))
317			goto nla_put_failure;
318		if (nla_put(skb, TCA_VLAN_PUSH_ETH_SRC, ETH_ALEN,
319			    p->tcfv_push_src))
320			goto nla_put_failure;
321	}
322
323	tcf_tm_dump(&t, &v->tcf_tm);
324	if (nla_put_64bit(skb, TCA_VLAN_TM, sizeof(t), &t, TCA_VLAN_PAD))
325		goto nla_put_failure;
326	rcu_read_unlock();
327
328	return skb->len;
329
330nla_put_failure:
331	rcu_read_unlock();
332	nlmsg_trim(skb, b);
333	return -1;
334}
335
336static void tcf_vlan_stats_update(struct tc_action *a, u64 bytes, u64 packets,
337				  u64 drops, u64 lastuse, bool hw)
338{
339	struct tcf_vlan *v = to_vlan(a);
340	struct tcf_t *tm = &v->tcf_tm;
341
342	tcf_action_update_stats(a, bytes, packets, drops, hw);
343	tm->lastuse = max_t(u64, tm->lastuse, lastuse);
344}
345
346static size_t tcf_vlan_get_fill_size(const struct tc_action *act)
347{
348	return nla_total_size(sizeof(struct tc_vlan))
349		+ nla_total_size(sizeof(u16)) /* TCA_VLAN_PUSH_VLAN_ID */
350		+ nla_total_size(sizeof(u16)) /* TCA_VLAN_PUSH_VLAN_PROTOCOL */
351		+ nla_total_size(sizeof(u8)); /* TCA_VLAN_PUSH_VLAN_PRIORITY */
352}
353
354static int tcf_vlan_offload_act_setup(struct tc_action *act, void *entry_data,
355				      u32 *index_inc, bool bind,
356				      struct netlink_ext_ack *extack)
357{
358	if (bind) {
359		struct flow_action_entry *entry = entry_data;
360
361		switch (tcf_vlan_action(act)) {
362		case TCA_VLAN_ACT_PUSH:
363			entry->id = FLOW_ACTION_VLAN_PUSH;
364			entry->vlan.vid = tcf_vlan_push_vid(act);
365			entry->vlan.proto = tcf_vlan_push_proto(act);
366			entry->vlan.prio = tcf_vlan_push_prio(act);
367			break;
368		case TCA_VLAN_ACT_POP:
369			entry->id = FLOW_ACTION_VLAN_POP;
370			break;
371		case TCA_VLAN_ACT_MODIFY:
372			entry->id = FLOW_ACTION_VLAN_MANGLE;
373			entry->vlan.vid = tcf_vlan_push_vid(act);
374			entry->vlan.proto = tcf_vlan_push_proto(act);
375			entry->vlan.prio = tcf_vlan_push_prio(act);
376			break;
377		case TCA_VLAN_ACT_POP_ETH:
378			entry->id = FLOW_ACTION_VLAN_POP_ETH;
379			break;
380		case TCA_VLAN_ACT_PUSH_ETH:
381			entry->id = FLOW_ACTION_VLAN_PUSH_ETH;
382			tcf_vlan_push_eth(entry->vlan_push_eth.src, entry->vlan_push_eth.dst, act);
383			break;
384		default:
385			NL_SET_ERR_MSG_MOD(extack, "Unsupported vlan action mode offload");
386			return -EOPNOTSUPP;
387		}
388		*index_inc = 1;
389	} else {
390		struct flow_offload_action *fl_action = entry_data;
391
392		switch (tcf_vlan_action(act)) {
393		case TCA_VLAN_ACT_PUSH:
394			fl_action->id = FLOW_ACTION_VLAN_PUSH;
395			break;
396		case TCA_VLAN_ACT_POP:
397			fl_action->id = FLOW_ACTION_VLAN_POP;
398			break;
399		case TCA_VLAN_ACT_MODIFY:
400			fl_action->id = FLOW_ACTION_VLAN_MANGLE;
401			break;
402		case TCA_VLAN_ACT_POP_ETH:
403			fl_action->id = FLOW_ACTION_VLAN_POP_ETH;
404			break;
405		case TCA_VLAN_ACT_PUSH_ETH:
406			fl_action->id = FLOW_ACTION_VLAN_PUSH_ETH;
407			break;
408		default:
409			return -EOPNOTSUPP;
410		}
411	}
412
413	return 0;
414}
415
416static struct tc_action_ops act_vlan_ops = {
417	.kind		=	"vlan",
418	.id		=	TCA_ID_VLAN,
419	.owner		=	THIS_MODULE,
420	.act		=	tcf_vlan_act,
421	.dump		=	tcf_vlan_dump,
422	.init		=	tcf_vlan_init,
423	.cleanup	=	tcf_vlan_cleanup,
424	.stats_update	=	tcf_vlan_stats_update,
425	.get_fill_size	=	tcf_vlan_get_fill_size,
426	.offload_act_setup =	tcf_vlan_offload_act_setup,
427	.size		=	sizeof(struct tcf_vlan),
428};
429MODULE_ALIAS_NET_ACT("vlan");
430
431static __net_init int vlan_init_net(struct net *net)
432{
433	struct tc_action_net *tn = net_generic(net, act_vlan_ops.net_id);
434
435	return tc_action_net_init(net, tn, &act_vlan_ops);
436}
437
438static void __net_exit vlan_exit_net(struct list_head *net_list)
439{
440	tc_action_net_exit(net_list, act_vlan_ops.net_id);
441}
442
443static struct pernet_operations vlan_net_ops = {
444	.init = vlan_init_net,
445	.exit_batch = vlan_exit_net,
446	.id   = &act_vlan_ops.net_id,
447	.size = sizeof(struct tc_action_net),
448};
449
450static int __init vlan_init_module(void)
451{
452	return tcf_register_action(&act_vlan_ops, &vlan_net_ops);
453}
454
455static void __exit vlan_cleanup_module(void)
456{
457	tcf_unregister_action(&act_vlan_ops, &vlan_net_ops);
458}
459
460module_init(vlan_init_module);
461module_exit(vlan_cleanup_module);
462
463MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>");
464MODULE_DESCRIPTION("vlan manipulation actions");
465MODULE_LICENSE("GPL v2");
~/ Read-only file preview.