12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2c7e2b968SJiri Pirko /*
3c7e2b968SJiri Pirko * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
4c7e2b968SJiri Pirko */
5c7e2b968SJiri Pirko
6c7e2b968SJiri Pirko #include <linux/module.h>
7c7e2b968SJiri Pirko #include <linux/init.h>
8c7e2b968SJiri Pirko #include <linux/kernel.h>
9c7e2b968SJiri Pirko #include <linux/skbuff.h>
10c7e2b968SJiri Pirko #include <linux/rtnetlink.h>
11c7e2b968SJiri Pirko #include <linux/if_vlan.h>
12c7e2b968SJiri Pirko #include <net/netlink.h>
13c7e2b968SJiri Pirko #include <net/pkt_sched.h>
147e0c8892SDavide Caratti #include <net/pkt_cls.h>
15*871cf386SPedro Tammela #include <net/tc_wrapper.h>
16c7e2b968SJiri Pirko
17c7e2b968SJiri Pirko #include <linux/tc_act/tc_vlan.h>
18c7e2b968SJiri Pirko #include <net/tc_act/tc_vlan.h>
19c7e2b968SJiri Pirko
20a85a970aSWANG Cong static struct tc_action_ops act_vlan_ops;
21ddf97ccdSWANG Cong
tcf_vlan_act(struct sk_buff * skb,const struct tc_action * a,struct tcf_result * res)22*871cf386SPedro Tammela TC_INDIRECT_SCOPE int tcf_vlan_act(struct sk_buff *skb,
23*871cf386SPedro Tammela const struct tc_action *a,
24c7e2b968SJiri Pirko struct tcf_result *res)
25c7e2b968SJiri Pirko {
26a85a970aSWANG Cong struct tcf_vlan *v = to_vlan(a);
274c5b9d96SManish Kurup struct tcf_vlan_params *p;
28c7e2b968SJiri Pirko int action;
29c7e2b968SJiri Pirko int err;
3045a497f2SShmulik Ladkani u16 tci;
31c7e2b968SJiri Pirko
329c4a4e48SJamal Hadi Salim tcf_lastuse_update(&v->tcf_tm);
335e1ad95bSVlad Buslov tcf_action_update_bstats(&v->common, skb);
34e0496cbbSManish Kurup
35f39acc84SShmulik Ladkani /* Ensure 'data' points at mac_header prior calling vlan manipulating
36f39acc84SShmulik Ladkani * functions.
37f39acc84SShmulik Ladkani */
38f39acc84SShmulik Ladkani if (skb_at_tc_ingress(skb))
39f39acc84SShmulik Ladkani skb_push_rcsum(skb, skb->mac_len);
40f39acc84SShmulik Ladkani
414c5b9d96SManish Kurup action = READ_ONCE(v->tcf_action);
424c5b9d96SManish Kurup
437fd4b288SPaolo Abeni p = rcu_dereference_bh(v->vlan_p);
444c5b9d96SManish Kurup
454c5b9d96SManish Kurup switch (p->tcfv_action) {
46c7e2b968SJiri Pirko case TCA_VLAN_ACT_POP:
47c7e2b968SJiri Pirko err = skb_vlan_pop(skb);
48c7e2b968SJiri Pirko if (err)
49c7e2b968SJiri Pirko goto drop;
50c7e2b968SJiri Pirko break;
51c7e2b968SJiri Pirko case TCA_VLAN_ACT_PUSH:
524c5b9d96SManish Kurup err = skb_vlan_push(skb, p->tcfv_push_proto, p->tcfv_push_vid |
534c5b9d96SManish Kurup (p->tcfv_push_prio << VLAN_PRIO_SHIFT));
54c7e2b968SJiri Pirko if (err)
55c7e2b968SJiri Pirko goto drop;
56c7e2b968SJiri Pirko break;
5745a497f2SShmulik Ladkani case TCA_VLAN_ACT_MODIFY:
5845a497f2SShmulik Ladkani /* No-op if no vlan tag (either hw-accel or in-payload) */
5945a497f2SShmulik Ladkani if (!skb_vlan_tagged(skb))
607fd4b288SPaolo Abeni goto out;
6145a497f2SShmulik Ladkani /* extract existing tag (and guarantee no hw-accel tag) */
6245a497f2SShmulik Ladkani if (skb_vlan_tag_present(skb)) {
6345a497f2SShmulik Ladkani tci = skb_vlan_tag_get(skb);
64b1817524SMichał Mirosław __vlan_hwaccel_clear_tag(skb);
6545a497f2SShmulik Ladkani } else {
6645a497f2SShmulik Ladkani /* in-payload vlan tag, pop it */
6745a497f2SShmulik Ladkani err = __skb_vlan_pop(skb, &tci);
6845a497f2SShmulik Ladkani if (err)
6945a497f2SShmulik Ladkani goto drop;
7045a497f2SShmulik Ladkani }
7145a497f2SShmulik Ladkani /* replace the vid */
724c5b9d96SManish Kurup tci = (tci & ~VLAN_VID_MASK) | p->tcfv_push_vid;
7345a497f2SShmulik Ladkani /* replace prio bits, if tcfv_push_prio specified */
749c5eee0aSBoris Sukholitko if (p->tcfv_push_prio_exists) {
7545a497f2SShmulik Ladkani tci &= ~VLAN_PRIO_MASK;
764c5b9d96SManish Kurup tci |= p->tcfv_push_prio << VLAN_PRIO_SHIFT;
7745a497f2SShmulik Ladkani }
7845a497f2SShmulik Ladkani /* put updated tci as hwaccel tag */
794c5b9d96SManish Kurup __vlan_hwaccel_put_tag(skb, p->tcfv_push_proto, tci);
8045a497f2SShmulik Ladkani break;
8119fbcb36SGuillaume Nault case TCA_VLAN_ACT_POP_ETH:
8219fbcb36SGuillaume Nault err = skb_eth_pop(skb);
8319fbcb36SGuillaume Nault if (err)
8419fbcb36SGuillaume Nault goto drop;
8519fbcb36SGuillaume Nault break;
8619fbcb36SGuillaume Nault case TCA_VLAN_ACT_PUSH_ETH:
8719fbcb36SGuillaume Nault err = skb_eth_push(skb, p->tcfv_push_dst, p->tcfv_push_src);
8819fbcb36SGuillaume Nault if (err)
8919fbcb36SGuillaume Nault goto drop;
9019fbcb36SGuillaume Nault break;
91c7e2b968SJiri Pirko default:
92c7e2b968SJiri Pirko BUG();
93c7e2b968SJiri Pirko }
94c7e2b968SJiri Pirko
957fd4b288SPaolo Abeni out:
96f39acc84SShmulik Ladkani if (skb_at_tc_ingress(skb))
97f39acc84SShmulik Ladkani skb_pull_rcsum(skb, skb->mac_len);
98f39acc84SShmulik Ladkani
99c7e2b968SJiri Pirko return action;
1007fd4b288SPaolo Abeni
1017fd4b288SPaolo Abeni drop:
10226b537a8SVlad Buslov tcf_action_inc_drop_qstats(&v->common);
1037fd4b288SPaolo Abeni return TC_ACT_SHOT;
104c7e2b968SJiri Pirko }
105c7e2b968SJiri Pirko
106c7e2b968SJiri Pirko static const struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = {
10719fbcb36SGuillaume Nault [TCA_VLAN_UNSPEC] = { .strict_start_type = TCA_VLAN_PUSH_ETH_DST },
108c7e2b968SJiri Pirko [TCA_VLAN_PARMS] = { .len = sizeof(struct tc_vlan) },
109c7e2b968SJiri Pirko [TCA_VLAN_PUSH_VLAN_ID] = { .type = NLA_U16 },
110c7e2b968SJiri Pirko [TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NLA_U16 },
111956af371SHadar Hen Zion [TCA_VLAN_PUSH_VLAN_PRIORITY] = { .type = NLA_U8 },
11219fbcb36SGuillaume Nault [TCA_VLAN_PUSH_ETH_DST] = NLA_POLICY_ETH_ADDR,
11319fbcb36SGuillaume Nault [TCA_VLAN_PUSH_ETH_SRC] = NLA_POLICY_ETH_ADDR,
114c7e2b968SJiri Pirko };
115c7e2b968SJiri Pirko
tcf_vlan_init(struct net * net,struct nlattr * nla,struct nlattr * est,struct tc_action ** a,struct tcf_proto * tp,u32 flags,struct netlink_ext_ack * extack)116c7e2b968SJiri Pirko static int tcf_vlan_init(struct net *net, struct nlattr *nla,
117a85a970aSWANG Cong struct nlattr *est, struct tc_action **a,
118abbb0d33SVlad Buslov struct tcf_proto *tp, u32 flags,
119abbb0d33SVlad Buslov struct netlink_ext_ack *extack)
120c7e2b968SJiri Pirko {
121acd0a7abSZhengchao Shao struct tc_action_net *tn = net_generic(net, act_vlan_ops.net_id);
122695176bfSCong Wang bool bind = flags & TCA_ACT_FLAGS_BIND;
123c7e2b968SJiri Pirko struct nlattr *tb[TCA_VLAN_MAX + 1];
1247e0c8892SDavide Caratti struct tcf_chain *goto_ch = NULL;
1259c5eee0aSBoris Sukholitko bool push_prio_exists = false;
126764e9a24SVlad Buslov struct tcf_vlan_params *p;
127c7e2b968SJiri Pirko struct tc_vlan *parm;
128c7e2b968SJiri Pirko struct tcf_vlan *v;
129c7e2b968SJiri Pirko int action;
13094cb5492SDavide Caratti u16 push_vid = 0;
131c7e2b968SJiri Pirko __be16 push_proto = 0;
132956af371SHadar Hen Zion u8 push_prio = 0;
133b2313077SWANG Cong bool exists = false;
134b2313077SWANG Cong int ret = 0, err;
1357be8ef2cSDmytro Linkin u32 index;
136c7e2b968SJiri Pirko
137c7e2b968SJiri Pirko if (!nla)
138c7e2b968SJiri Pirko return -EINVAL;
139c7e2b968SJiri Pirko
1408cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, TCA_VLAN_MAX, nla, vlan_policy,
1418cb08174SJohannes Berg NULL);
142c7e2b968SJiri Pirko if (err < 0)
143c7e2b968SJiri Pirko return err;
144c7e2b968SJiri Pirko
145c7e2b968SJiri Pirko if (!tb[TCA_VLAN_PARMS])
146c7e2b968SJiri Pirko return -EINVAL;
147c7e2b968SJiri Pirko parm = nla_data(tb[TCA_VLAN_PARMS]);
1487be8ef2cSDmytro Linkin index = parm->index;
1497be8ef2cSDmytro Linkin err = tcf_idr_check_alloc(tn, &index, a, bind);
1500190c1d4SVlad Buslov if (err < 0)
1510190c1d4SVlad Buslov return err;
1520190c1d4SVlad Buslov exists = err;
1535026c9b1SJamal Hadi Salim if (exists && bind)
1545026c9b1SJamal Hadi Salim return 0;
1555026c9b1SJamal Hadi Salim
156c7e2b968SJiri Pirko switch (parm->v_action) {
157c7e2b968SJiri Pirko case TCA_VLAN_ACT_POP:
158c7e2b968SJiri Pirko break;
159c7e2b968SJiri Pirko case TCA_VLAN_ACT_PUSH:
16045a497f2SShmulik Ladkani case TCA_VLAN_ACT_MODIFY:
1615026c9b1SJamal Hadi Salim if (!tb[TCA_VLAN_PUSH_VLAN_ID]) {
1625026c9b1SJamal Hadi Salim if (exists)
16365a206c0SChris Mi tcf_idr_release(*a, bind);
1640190c1d4SVlad Buslov else
1657be8ef2cSDmytro Linkin tcf_idr_cleanup(tn, index);
166c7e2b968SJiri Pirko return -EINVAL;
1675026c9b1SJamal Hadi Salim }
168c7e2b968SJiri Pirko push_vid = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_ID]);
1695026c9b1SJamal Hadi Salim if (push_vid >= VLAN_VID_MASK) {
1705026c9b1SJamal Hadi Salim if (exists)
17165a206c0SChris Mi tcf_idr_release(*a, bind);
1720190c1d4SVlad Buslov else
1737be8ef2cSDmytro Linkin tcf_idr_cleanup(tn, index);
174c7e2b968SJiri Pirko return -ERANGE;
1755026c9b1SJamal Hadi Salim }
176c7e2b968SJiri Pirko
177c7e2b968SJiri Pirko if (tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]) {
178c7e2b968SJiri Pirko push_proto = nla_get_be16(tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]);
179c7e2b968SJiri Pirko switch (push_proto) {
180c7e2b968SJiri Pirko case htons(ETH_P_8021Q):
181c7e2b968SJiri Pirko case htons(ETH_P_8021AD):
182c7e2b968SJiri Pirko break;
183c7e2b968SJiri Pirko default:
1845a4931aeSDavide Caratti if (exists)
1855a4931aeSDavide Caratti tcf_idr_release(*a, bind);
1860190c1d4SVlad Buslov else
1877be8ef2cSDmytro Linkin tcf_idr_cleanup(tn, index);
188c7e2b968SJiri Pirko return -EPROTONOSUPPORT;
189c7e2b968SJiri Pirko }
190c7e2b968SJiri Pirko } else {
191c7e2b968SJiri Pirko push_proto = htons(ETH_P_8021Q);
192c7e2b968SJiri Pirko }
193956af371SHadar Hen Zion
1949c5eee0aSBoris Sukholitko push_prio_exists = !!tb[TCA_VLAN_PUSH_VLAN_PRIORITY];
1959c5eee0aSBoris Sukholitko if (push_prio_exists)
196956af371SHadar Hen Zion push_prio = nla_get_u8(tb[TCA_VLAN_PUSH_VLAN_PRIORITY]);
197c7e2b968SJiri Pirko break;
19819fbcb36SGuillaume Nault case TCA_VLAN_ACT_POP_ETH:
19919fbcb36SGuillaume Nault break;
20019fbcb36SGuillaume Nault case TCA_VLAN_ACT_PUSH_ETH:
20119fbcb36SGuillaume Nault if (!tb[TCA_VLAN_PUSH_ETH_DST] || !tb[TCA_VLAN_PUSH_ETH_SRC]) {
20219fbcb36SGuillaume Nault if (exists)
20319fbcb36SGuillaume Nault tcf_idr_release(*a, bind);
20419fbcb36SGuillaume Nault else
20519fbcb36SGuillaume Nault tcf_idr_cleanup(tn, index);
20619fbcb36SGuillaume Nault return -EINVAL;
20719fbcb36SGuillaume Nault }
20819fbcb36SGuillaume Nault break;
209c7e2b968SJiri Pirko default:
2105026c9b1SJamal Hadi Salim if (exists)
21165a206c0SChris Mi tcf_idr_release(*a, bind);
2120190c1d4SVlad Buslov else
2137be8ef2cSDmytro Linkin tcf_idr_cleanup(tn, index);
214c7e2b968SJiri Pirko return -EINVAL;
215c7e2b968SJiri Pirko }
216c7e2b968SJiri Pirko action = parm->v_action;
217c7e2b968SJiri Pirko
2185026c9b1SJamal Hadi Salim if (!exists) {
219e3822678SVlad Buslov ret = tcf_idr_create_from_flags(tn, index, est, a,
220e3822678SVlad Buslov &act_vlan_ops, bind, flags);
2210190c1d4SVlad Buslov if (ret) {
2227be8ef2cSDmytro Linkin tcf_idr_cleanup(tn, index);
223c7e2b968SJiri Pirko return ret;
2240190c1d4SVlad Buslov }
225c7e2b968SJiri Pirko
226c7e2b968SJiri Pirko ret = ACT_P_CREATED;
227695176bfSCong Wang } else if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
22865a206c0SChris Mi tcf_idr_release(*a, bind);
229c7e2b968SJiri Pirko return -EEXIST;
230c7e2b968SJiri Pirko }
231c7e2b968SJiri Pirko
2327e0c8892SDavide Caratti err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
2337e0c8892SDavide Caratti if (err < 0)
2347e0c8892SDavide Caratti goto release_idr;
2357e0c8892SDavide Caratti
236a85a970aSWANG Cong v = to_vlan(*a);
237c7e2b968SJiri Pirko
2384c5b9d96SManish Kurup p = kzalloc(sizeof(*p), GFP_KERNEL);
2394c5b9d96SManish Kurup if (!p) {
2407e0c8892SDavide Caratti err = -ENOMEM;
2417e0c8892SDavide Caratti goto put_chain;
2424c5b9d96SManish Kurup }
243c7e2b968SJiri Pirko
2444c5b9d96SManish Kurup p->tcfv_action = action;
2454c5b9d96SManish Kurup p->tcfv_push_vid = push_vid;
2464c5b9d96SManish Kurup p->tcfv_push_prio = push_prio;
2479c5eee0aSBoris Sukholitko p->tcfv_push_prio_exists = push_prio_exists || action == TCA_VLAN_ACT_PUSH;
2484c5b9d96SManish Kurup p->tcfv_push_proto = push_proto;
2494c5b9d96SManish Kurup
25019fbcb36SGuillaume Nault if (action == TCA_VLAN_ACT_PUSH_ETH) {
25119fbcb36SGuillaume Nault nla_memcpy(&p->tcfv_push_dst, tb[TCA_VLAN_PUSH_ETH_DST],
25219fbcb36SGuillaume Nault ETH_ALEN);
25319fbcb36SGuillaume Nault nla_memcpy(&p->tcfv_push_src, tb[TCA_VLAN_PUSH_ETH_SRC],
25419fbcb36SGuillaume Nault ETH_ALEN);
25519fbcb36SGuillaume Nault }
25619fbcb36SGuillaume Nault
257653cd284SVlad Buslov spin_lock_bh(&v->tcf_lock);
2587e0c8892SDavide Caratti goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
259445d3749SPaul E. McKenney p = rcu_replace_pointer(v->vlan_p, p, lockdep_is_held(&v->tcf_lock));
260653cd284SVlad Buslov spin_unlock_bh(&v->tcf_lock);
2614c5b9d96SManish Kurup
2627e0c8892SDavide Caratti if (goto_ch)
2637e0c8892SDavide Caratti tcf_chain_put_by_act(goto_ch);
264764e9a24SVlad Buslov if (p)
265764e9a24SVlad Buslov kfree_rcu(p, rcu);
266c7e2b968SJiri Pirko
267c7e2b968SJiri Pirko return ret;
2687e0c8892SDavide Caratti put_chain:
2697e0c8892SDavide Caratti if (goto_ch)
2707e0c8892SDavide Caratti tcf_chain_put_by_act(goto_ch);
2717e0c8892SDavide Caratti release_idr:
2727e0c8892SDavide Caratti tcf_idr_release(*a, bind);
2737e0c8892SDavide Caratti return err;
274c7e2b968SJiri Pirko }
275c7e2b968SJiri Pirko
tcf_vlan_cleanup(struct tc_action * a)2769a63b255SCong Wang static void tcf_vlan_cleanup(struct tc_action *a)
2774c5b9d96SManish Kurup {
2784c5b9d96SManish Kurup struct tcf_vlan *v = to_vlan(a);
2794c5b9d96SManish Kurup struct tcf_vlan_params *p;
2804c5b9d96SManish Kurup
2814c5b9d96SManish Kurup p = rcu_dereference_protected(v->vlan_p, 1);
2821edf8abeSDavide Caratti if (p)
2834c5b9d96SManish Kurup kfree_rcu(p, rcu);
2844c5b9d96SManish Kurup }
2854c5b9d96SManish Kurup
tcf_vlan_dump(struct sk_buff * skb,struct tc_action * a,int bind,int ref)286c7e2b968SJiri Pirko static int tcf_vlan_dump(struct sk_buff *skb, struct tc_action *a,
287c7e2b968SJiri Pirko int bind, int ref)
288c7e2b968SJiri Pirko {
289c7e2b968SJiri Pirko unsigned char *b = skb_tail_pointer(skb);
290a85a970aSWANG Cong struct tcf_vlan *v = to_vlan(a);
291764e9a24SVlad Buslov struct tcf_vlan_params *p;
292c7e2b968SJiri Pirko struct tc_vlan opt = {
293c7e2b968SJiri Pirko .index = v->tcf_index,
294036bb443SVlad Buslov .refcnt = refcount_read(&v->tcf_refcnt) - ref,
295036bb443SVlad Buslov .bindcnt = atomic_read(&v->tcf_bindcnt) - bind,
296c7e2b968SJiri Pirko };
297c7e2b968SJiri Pirko struct tcf_t t;
298c7e2b968SJiri Pirko
299653cd284SVlad Buslov spin_lock_bh(&v->tcf_lock);
300764e9a24SVlad Buslov opt.action = v->tcf_action;
301764e9a24SVlad Buslov p = rcu_dereference_protected(v->vlan_p, lockdep_is_held(&v->tcf_lock));
302764e9a24SVlad Buslov opt.v_action = p->tcfv_action;
303c7e2b968SJiri Pirko if (nla_put(skb, TCA_VLAN_PARMS, sizeof(opt), &opt))
304c7e2b968SJiri Pirko goto nla_put_failure;
305c7e2b968SJiri Pirko
3064c5b9d96SManish Kurup if ((p->tcfv_action == TCA_VLAN_ACT_PUSH ||
3074c5b9d96SManish Kurup p->tcfv_action == TCA_VLAN_ACT_MODIFY) &&
3084c5b9d96SManish Kurup (nla_put_u16(skb, TCA_VLAN_PUSH_VLAN_ID, p->tcfv_push_vid) ||
3090b0f43feSJamal Hadi Salim nla_put_be16(skb, TCA_VLAN_PUSH_VLAN_PROTOCOL,
3104c5b9d96SManish Kurup p->tcfv_push_proto) ||
3118323b20fSBoris Sukholitko (p->tcfv_push_prio_exists &&
3128323b20fSBoris Sukholitko nla_put_u8(skb, TCA_VLAN_PUSH_VLAN_PRIORITY, p->tcfv_push_prio))))
313c7e2b968SJiri Pirko goto nla_put_failure;
314c7e2b968SJiri Pirko
31519fbcb36SGuillaume Nault if (p->tcfv_action == TCA_VLAN_ACT_PUSH_ETH) {
31619fbcb36SGuillaume Nault if (nla_put(skb, TCA_VLAN_PUSH_ETH_DST, ETH_ALEN,
31719fbcb36SGuillaume Nault p->tcfv_push_dst))
31819fbcb36SGuillaume Nault goto nla_put_failure;
31919fbcb36SGuillaume Nault if (nla_put(skb, TCA_VLAN_PUSH_ETH_SRC, ETH_ALEN,
32019fbcb36SGuillaume Nault p->tcfv_push_src))
32119fbcb36SGuillaume Nault goto nla_put_failure;
32219fbcb36SGuillaume Nault }
32319fbcb36SGuillaume Nault
32448d8ee16SJamal Hadi Salim tcf_tm_dump(&t, &v->tcf_tm);
3259854518eSNicolas Dichtel if (nla_put_64bit(skb, TCA_VLAN_TM, sizeof(t), &t, TCA_VLAN_PAD))
326c7e2b968SJiri Pirko goto nla_put_failure;
327653cd284SVlad Buslov spin_unlock_bh(&v->tcf_lock);
328764e9a24SVlad Buslov
329c7e2b968SJiri Pirko return skb->len;
330c7e2b968SJiri Pirko
331c7e2b968SJiri Pirko nla_put_failure:
332653cd284SVlad Buslov spin_unlock_bh(&v->tcf_lock);
333c7e2b968SJiri Pirko nlmsg_trim(skb, b);
334c7e2b968SJiri Pirko return -1;
335c7e2b968SJiri Pirko }
336c7e2b968SJiri Pirko
tcf_vlan_stats_update(struct tc_action * a,u64 bytes,u64 packets,u64 drops,u64 lastuse,bool hw)3374b61d3e8SPo Liu static void tcf_vlan_stats_update(struct tc_action *a, u64 bytes, u64 packets,
3384b61d3e8SPo Liu u64 drops, u64 lastuse, bool hw)
339fa730a3bSJiri Pirko {
340fa730a3bSJiri Pirko struct tcf_vlan *v = to_vlan(a);
341fa730a3bSJiri Pirko struct tcf_t *tm = &v->tcf_tm;
342fa730a3bSJiri Pirko
3434b61d3e8SPo Liu tcf_action_update_stats(a, bytes, packets, drops, hw);
344fa730a3bSJiri Pirko tm->lastuse = max_t(u64, tm->lastuse, lastuse);
345fa730a3bSJiri Pirko }
346fa730a3bSJiri Pirko
tcf_vlan_get_fill_size(const struct tc_action * act)347b35475c5SRoman Mashak static size_t tcf_vlan_get_fill_size(const struct tc_action *act)
348b35475c5SRoman Mashak {
349b35475c5SRoman Mashak return nla_total_size(sizeof(struct tc_vlan))
350b35475c5SRoman Mashak + nla_total_size(sizeof(u16)) /* TCA_VLAN_PUSH_VLAN_ID */
351b35475c5SRoman Mashak + nla_total_size(sizeof(u16)) /* TCA_VLAN_PUSH_VLAN_PROTOCOL */
352b35475c5SRoman Mashak + nla_total_size(sizeof(u8)); /* TCA_VLAN_PUSH_VLAN_PRIORITY */
353b35475c5SRoman Mashak }
354b35475c5SRoman Mashak
tcf_vlan_offload_act_setup(struct tc_action * act,void * entry_data,u32 * index_inc,bool bind,struct netlink_ext_ack * extack)355c54e1d92SBaowen Zheng static int tcf_vlan_offload_act_setup(struct tc_action *act, void *entry_data,
356c2ccf84eSIdo Schimmel u32 *index_inc, bool bind,
357c2ccf84eSIdo Schimmel struct netlink_ext_ack *extack)
358c54e1d92SBaowen Zheng {
359c54e1d92SBaowen Zheng if (bind) {
360c54e1d92SBaowen Zheng struct flow_action_entry *entry = entry_data;
361c54e1d92SBaowen Zheng
362c54e1d92SBaowen Zheng switch (tcf_vlan_action(act)) {
363c54e1d92SBaowen Zheng case TCA_VLAN_ACT_PUSH:
364c54e1d92SBaowen Zheng entry->id = FLOW_ACTION_VLAN_PUSH;
365c54e1d92SBaowen Zheng entry->vlan.vid = tcf_vlan_push_vid(act);
366c54e1d92SBaowen Zheng entry->vlan.proto = tcf_vlan_push_proto(act);
367c54e1d92SBaowen Zheng entry->vlan.prio = tcf_vlan_push_prio(act);
368c54e1d92SBaowen Zheng break;
369c54e1d92SBaowen Zheng case TCA_VLAN_ACT_POP:
370c54e1d92SBaowen Zheng entry->id = FLOW_ACTION_VLAN_POP;
371c54e1d92SBaowen Zheng break;
372c54e1d92SBaowen Zheng case TCA_VLAN_ACT_MODIFY:
373c54e1d92SBaowen Zheng entry->id = FLOW_ACTION_VLAN_MANGLE;
374c54e1d92SBaowen Zheng entry->vlan.vid = tcf_vlan_push_vid(act);
375c54e1d92SBaowen Zheng entry->vlan.proto = tcf_vlan_push_proto(act);
376c54e1d92SBaowen Zheng entry->vlan.prio = tcf_vlan_push_prio(act);
377c54e1d92SBaowen Zheng break;
378ab95465cSMaor Dickman case TCA_VLAN_ACT_POP_ETH:
379ab95465cSMaor Dickman entry->id = FLOW_ACTION_VLAN_POP_ETH;
380ab95465cSMaor Dickman break;
381ab95465cSMaor Dickman case TCA_VLAN_ACT_PUSH_ETH:
382ab95465cSMaor Dickman entry->id = FLOW_ACTION_VLAN_PUSH_ETH;
383ab95465cSMaor Dickman tcf_vlan_push_eth(entry->vlan_push_eth.src, entry->vlan_push_eth.dst, act);
384ab95465cSMaor Dickman break;
385c54e1d92SBaowen Zheng default:
386f8fab316SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Unsupported vlan action mode offload");
387c54e1d92SBaowen Zheng return -EOPNOTSUPP;
388c54e1d92SBaowen Zheng }
389c54e1d92SBaowen Zheng *index_inc = 1;
390c54e1d92SBaowen Zheng } else {
3918cbfe939SBaowen Zheng struct flow_offload_action *fl_action = entry_data;
3928cbfe939SBaowen Zheng
3938cbfe939SBaowen Zheng switch (tcf_vlan_action(act)) {
3948cbfe939SBaowen Zheng case TCA_VLAN_ACT_PUSH:
3958cbfe939SBaowen Zheng fl_action->id = FLOW_ACTION_VLAN_PUSH;
3968cbfe939SBaowen Zheng break;
3978cbfe939SBaowen Zheng case TCA_VLAN_ACT_POP:
3988cbfe939SBaowen Zheng fl_action->id = FLOW_ACTION_VLAN_POP;
3998cbfe939SBaowen Zheng break;
4008cbfe939SBaowen Zheng case TCA_VLAN_ACT_MODIFY:
4018cbfe939SBaowen Zheng fl_action->id = FLOW_ACTION_VLAN_MANGLE;
4028cbfe939SBaowen Zheng break;
403ab95465cSMaor Dickman case TCA_VLAN_ACT_POP_ETH:
404ab95465cSMaor Dickman fl_action->id = FLOW_ACTION_VLAN_POP_ETH;
405ab95465cSMaor Dickman break;
406ab95465cSMaor Dickman case TCA_VLAN_ACT_PUSH_ETH:
407ab95465cSMaor Dickman fl_action->id = FLOW_ACTION_VLAN_PUSH_ETH;
408ab95465cSMaor Dickman break;
4098cbfe939SBaowen Zheng default:
410c54e1d92SBaowen Zheng return -EOPNOTSUPP;
411c54e1d92SBaowen Zheng }
4128cbfe939SBaowen Zheng }
413c54e1d92SBaowen Zheng
414c54e1d92SBaowen Zheng return 0;
415c54e1d92SBaowen Zheng }
416c54e1d92SBaowen Zheng
417c7e2b968SJiri Pirko static struct tc_action_ops act_vlan_ops = {
418c7e2b968SJiri Pirko .kind = "vlan",
419eddd2cf1SEli Cohen .id = TCA_ID_VLAN,
420c7e2b968SJiri Pirko .owner = THIS_MODULE,
4218aa7f22eSJamal Hadi Salim .act = tcf_vlan_act,
422c7e2b968SJiri Pirko .dump = tcf_vlan_dump,
423c7e2b968SJiri Pirko .init = tcf_vlan_init,
4244c5b9d96SManish Kurup .cleanup = tcf_vlan_cleanup,
425fa730a3bSJiri Pirko .stats_update = tcf_vlan_stats_update,
426b35475c5SRoman Mashak .get_fill_size = tcf_vlan_get_fill_size,
427c54e1d92SBaowen Zheng .offload_act_setup = tcf_vlan_offload_act_setup,
428a85a970aSWANG Cong .size = sizeof(struct tcf_vlan),
429ddf97ccdSWANG Cong };
430ddf97ccdSWANG Cong
vlan_init_net(struct net * net)431ddf97ccdSWANG Cong static __net_init int vlan_init_net(struct net *net)
432ddf97ccdSWANG Cong {
433acd0a7abSZhengchao Shao struct tc_action_net *tn = net_generic(net, act_vlan_ops.net_id);
434ddf97ccdSWANG Cong
435981471bdSCong Wang return tc_action_net_init(net, tn, &act_vlan_ops);
436ddf97ccdSWANG Cong }
437ddf97ccdSWANG Cong
vlan_exit_net(struct list_head * net_list)438039af9c6SCong Wang static void __net_exit vlan_exit_net(struct list_head *net_list)
439ddf97ccdSWANG Cong {
440acd0a7abSZhengchao Shao tc_action_net_exit(net_list, act_vlan_ops.net_id);
441ddf97ccdSWANG Cong }
442ddf97ccdSWANG Cong
443ddf97ccdSWANG Cong static struct pernet_operations vlan_net_ops = {
444ddf97ccdSWANG Cong .init = vlan_init_net,
445039af9c6SCong Wang .exit_batch = vlan_exit_net,
446acd0a7abSZhengchao Shao .id = &act_vlan_ops.net_id,
447ddf97ccdSWANG Cong .size = sizeof(struct tc_action_net),
448c7e2b968SJiri Pirko };
449c7e2b968SJiri Pirko
vlan_init_module(void)450c7e2b968SJiri Pirko static int __init vlan_init_module(void)
451c7e2b968SJiri Pirko {
452ddf97ccdSWANG Cong return tcf_register_action(&act_vlan_ops, &vlan_net_ops);
453c7e2b968SJiri Pirko }
454c7e2b968SJiri Pirko
vlan_cleanup_module(void)455c7e2b968SJiri Pirko static void __exit vlan_cleanup_module(void)
456c7e2b968SJiri Pirko {
457ddf97ccdSWANG Cong tcf_unregister_action(&act_vlan_ops, &vlan_net_ops);
458c7e2b968SJiri Pirko }
459c7e2b968SJiri Pirko
460c7e2b968SJiri Pirko module_init(vlan_init_module);
461c7e2b968SJiri Pirko module_exit(vlan_cleanup_module);
462c7e2b968SJiri Pirko
463c7e2b968SJiri Pirko MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>");
464c7e2b968SJiri Pirko MODULE_DESCRIPTION("vlan manipulation actions");
465c7e2b968SJiri Pirko MODULE_LICENSE("GPL v2");
466