linux/net/sched/act_vlan.c
<<
>>
Prefs
   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
  16#include <linux/tc_act/tc_vlan.h>
  17#include <net/tc_act/tc_vlan.h>
  18
  19static unsigned int vlan_net_id;
  20static struct tc_action_ops act_vlan_ops;
  21
  22static int tcf_vlan_act(struct sk_buff *skb, const struct tc_action *a,
  23                        struct tcf_result *res)
  24{
  25        struct tcf_vlan *v = to_vlan(a);
  26        struct tcf_vlan_params *p;
  27        int action;
  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        action = READ_ONCE(v->tcf_action);
  41
  42        p = rcu_dereference_bh(v->vlan_p);
  43
  44        switch (p->tcfv_action) {
  45        case TCA_VLAN_ACT_POP:
  46                err = skb_vlan_pop(skb);
  47                if (err)
  48                        goto drop;
  49                break;
  50        case TCA_VLAN_ACT_PUSH:
  51                err = skb_vlan_push(skb, p->tcfv_push_proto, p->tcfv_push_vid |
  52                                    (p->tcfv_push_prio << VLAN_PRIO_SHIFT));
  53                if (err)
  54                        goto drop;
  55                break;
  56        case TCA_VLAN_ACT_MODIFY:
  57                /* No-op if no vlan tag (either hw-accel or in-payload) */
  58                if (!skb_vlan_tagged(skb))
  59                        goto out;
  60                /* extract existing tag (and guarantee no hw-accel tag) */
  61                if (skb_vlan_tag_present(skb)) {
  62                        tci = skb_vlan_tag_get(skb);
  63                        __vlan_hwaccel_clear_tag(skb);
  64                } else {
  65                        /* in-payload vlan tag, pop it */
  66                        err = __skb_vlan_pop(skb, &tci);
  67                        if (err)
  68                                goto drop;
  69                }
  70                /* replace the vid */
  71                tci = (tci & ~VLAN_VID_MASK) | p->tcfv_push_vid;
  72                /* replace prio bits, if tcfv_push_prio specified */
  73                if (p->tcfv_push_prio) {
  74                        tci &= ~VLAN_PRIO_MASK;
  75                        tci |= p->tcfv_push_prio << VLAN_PRIO_SHIFT;
  76                }
  77                /* put updated tci as hwaccel tag */
  78                __vlan_hwaccel_put_tag(skb, p->tcfv_push_proto, tci);
  79                break;
  80        case TCA_VLAN_ACT_POP_ETH:
  81                err = skb_eth_pop(skb);
  82                if (err)
  83                        goto drop;
  84                break;
  85        case TCA_VLAN_ACT_PUSH_ETH:
  86                err = skb_eth_push(skb, p->tcfv_push_dst, p->tcfv_push_src);
  87                if (err)
  88                        goto drop;
  89                break;
  90        default:
  91                BUG();
  92        }
  93
  94out:
  95        if (skb_at_tc_ingress(skb))
  96                skb_pull_rcsum(skb, skb->mac_len);
  97
  98        return action;
  99
 100drop:
 101        tcf_action_inc_drop_qstats(&v->common);
 102        return TC_ACT_SHOT;
 103}
 104
 105static const struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = {
 106        [TCA_VLAN_UNSPEC]               = { .strict_start_type = TCA_VLAN_PUSH_ETH_DST },
 107        [TCA_VLAN_PARMS]                = { .len = sizeof(struct tc_vlan) },
 108        [TCA_VLAN_PUSH_VLAN_ID]         = { .type = NLA_U16 },
 109        [TCA_VLAN_PUSH_VLAN_PROTOCOL]   = { .type = NLA_U16 },
 110        [TCA_VLAN_PUSH_VLAN_PRIORITY]   = { .type = NLA_U8 },
 111        [TCA_VLAN_PUSH_ETH_DST]         = NLA_POLICY_ETH_ADDR,
 112        [TCA_VLAN_PUSH_ETH_SRC]         = NLA_POLICY_ETH_ADDR,
 113};
 114
 115static int tcf_vlan_init(struct net *net, struct nlattr *nla,
 116                         struct nlattr *est, struct tc_action **a,
 117                         int ovr, int bind, bool rtnl_held,
 118                         struct tcf_proto *tp, u32 flags,
 119                         struct netlink_ext_ack *extack)
 120{
 121        struct tc_action_net *tn = net_generic(net, vlan_net_id);
 122        struct nlattr *tb[TCA_VLAN_MAX + 1];
 123        struct tcf_chain *goto_ch = NULL;
 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 0;
 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                if (tb[TCA_VLAN_PUSH_VLAN_PRIORITY])
 193                        push_prio = nla_get_u8(tb[TCA_VLAN_PUSH_VLAN_PRIORITY]);
 194                break;
 195        case TCA_VLAN_ACT_POP_ETH:
 196                break;
 197        case TCA_VLAN_ACT_PUSH_ETH:
 198                if (!tb[TCA_VLAN_PUSH_ETH_DST] || !tb[TCA_VLAN_PUSH_ETH_SRC]) {
 199                        if (exists)
 200                                tcf_idr_release(*a, bind);
 201                        else
 202                                tcf_idr_cleanup(tn, index);
 203                        return -EINVAL;
 204                }
 205                break;
 206        default:
 207                if (exists)
 208                        tcf_idr_release(*a, bind);
 209                else
 210                        tcf_idr_cleanup(tn, index);
 211                return -EINVAL;
 212        }
 213        action = parm->v_action;
 214
 215        if (!exists) {
 216                ret = tcf_idr_create_from_flags(tn, index, est, a,
 217                                                &act_vlan_ops, bind, flags);
 218                if (ret) {
 219                        tcf_idr_cleanup(tn, index);
 220                        return ret;
 221                }
 222
 223                ret = ACT_P_CREATED;
 224        } else if (!ovr) {
 225                tcf_idr_release(*a, bind);
 226                return -EEXIST;
 227        }
 228
 229        err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
 230        if (err < 0)
 231                goto release_idr;
 232
 233        v = to_vlan(*a);
 234
 235        p = kzalloc(sizeof(*p), GFP_KERNEL);
 236        if (!p) {
 237                err = -ENOMEM;
 238                goto put_chain;
 239        }
 240
 241        p->tcfv_action = action;
 242        p->tcfv_push_vid = push_vid;
 243        p->tcfv_push_prio = push_prio;
 244        p->tcfv_push_proto = push_proto;
 245
 246        if (action == TCA_VLAN_ACT_PUSH_ETH) {
 247                nla_memcpy(&p->tcfv_push_dst, tb[TCA_VLAN_PUSH_ETH_DST],
 248                           ETH_ALEN);
 249                nla_memcpy(&p->tcfv_push_src, tb[TCA_VLAN_PUSH_ETH_SRC],
 250                           ETH_ALEN);
 251        }
 252
 253        spin_lock_bh(&v->tcf_lock);
 254        goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
 255        p = rcu_replace_pointer(v->vlan_p, p, lockdep_is_held(&v->tcf_lock));
 256        spin_unlock_bh(&v->tcf_lock);
 257
 258        if (goto_ch)
 259                tcf_chain_put_by_act(goto_ch);
 260        if (p)
 261                kfree_rcu(p, rcu);
 262
 263        return ret;
 264put_chain:
 265        if (goto_ch)
 266                tcf_chain_put_by_act(goto_ch);
 267release_idr:
 268        tcf_idr_release(*a, bind);
 269        return err;
 270}
 271
 272static void tcf_vlan_cleanup(struct tc_action *a)
 273{
 274        struct tcf_vlan *v = to_vlan(a);
 275        struct tcf_vlan_params *p;
 276
 277        p = rcu_dereference_protected(v->vlan_p, 1);
 278        if (p)
 279                kfree_rcu(p, rcu);
 280}
 281
 282static int tcf_vlan_dump(struct sk_buff *skb, struct tc_action *a,
 283                         int bind, int ref)
 284{
 285        unsigned char *b = skb_tail_pointer(skb);
 286        struct tcf_vlan *v = to_vlan(a);
 287        struct tcf_vlan_params *p;
 288        struct tc_vlan opt = {
 289                .index    = v->tcf_index,
 290                .refcnt   = refcount_read(&v->tcf_refcnt) - ref,
 291                .bindcnt  = atomic_read(&v->tcf_bindcnt) - bind,
 292        };
 293        struct tcf_t t;
 294
 295        spin_lock_bh(&v->tcf_lock);
 296        opt.action = v->tcf_action;
 297        p = rcu_dereference_protected(v->vlan_p, lockdep_is_held(&v->tcf_lock));
 298        opt.v_action = p->tcfv_action;
 299        if (nla_put(skb, TCA_VLAN_PARMS, sizeof(opt), &opt))
 300                goto nla_put_failure;
 301
 302        if ((p->tcfv_action == TCA_VLAN_ACT_PUSH ||
 303             p->tcfv_action == TCA_VLAN_ACT_MODIFY) &&
 304            (nla_put_u16(skb, TCA_VLAN_PUSH_VLAN_ID, p->tcfv_push_vid) ||
 305             nla_put_be16(skb, TCA_VLAN_PUSH_VLAN_PROTOCOL,
 306                          p->tcfv_push_proto) ||
 307             (nla_put_u8(skb, TCA_VLAN_PUSH_VLAN_PRIORITY,
 308                                              p->tcfv_push_prio))))
 309                goto nla_put_failure;
 310
 311        if (p->tcfv_action == TCA_VLAN_ACT_PUSH_ETH) {
 312                if (nla_put(skb, TCA_VLAN_PUSH_ETH_DST, ETH_ALEN,
 313                            p->tcfv_push_dst))
 314                        goto nla_put_failure;
 315                if (nla_put(skb, TCA_VLAN_PUSH_ETH_SRC, ETH_ALEN,
 316                            p->tcfv_push_src))
 317                        goto nla_put_failure;
 318        }
 319
 320        tcf_tm_dump(&t, &v->tcf_tm);
 321        if (nla_put_64bit(skb, TCA_VLAN_TM, sizeof(t), &t, TCA_VLAN_PAD))
 322                goto nla_put_failure;
 323        spin_unlock_bh(&v->tcf_lock);
 324
 325        return skb->len;
 326
 327nla_put_failure:
 328        spin_unlock_bh(&v->tcf_lock);
 329        nlmsg_trim(skb, b);
 330        return -1;
 331}
 332
 333static int tcf_vlan_walker(struct net *net, struct sk_buff *skb,
 334                           struct netlink_callback *cb, int type,
 335                           const struct tc_action_ops *ops,
 336                           struct netlink_ext_ack *extack)
 337{
 338        struct tc_action_net *tn = net_generic(net, vlan_net_id);
 339
 340        return tcf_generic_walker(tn, skb, cb, type, ops, extack);
 341}
 342
 343static void tcf_vlan_stats_update(struct tc_action *a, u64 bytes, u64 packets,
 344                                  u64 drops, u64 lastuse, bool hw)
 345{
 346        struct tcf_vlan *v = to_vlan(a);
 347        struct tcf_t *tm = &v->tcf_tm;
 348
 349        tcf_action_update_stats(a, bytes, packets, drops, hw);
 350        tm->lastuse = max_t(u64, tm->lastuse, lastuse);
 351}
 352
 353static int tcf_vlan_search(struct net *net, struct tc_action **a, u32 index)
 354{
 355        struct tc_action_net *tn = net_generic(net, vlan_net_id);
 356
 357        return tcf_idr_search(tn, a, index);
 358}
 359
 360static size_t tcf_vlan_get_fill_size(const struct tc_action *act)
 361{
 362        return nla_total_size(sizeof(struct tc_vlan))
 363                + nla_total_size(sizeof(u16)) /* TCA_VLAN_PUSH_VLAN_ID */
 364                + nla_total_size(sizeof(u16)) /* TCA_VLAN_PUSH_VLAN_PROTOCOL */
 365                + nla_total_size(sizeof(u8)); /* TCA_VLAN_PUSH_VLAN_PRIORITY */
 366}
 367
 368static struct tc_action_ops act_vlan_ops = {
 369        .kind           =       "vlan",
 370        .id             =       TCA_ID_VLAN,
 371        .owner          =       THIS_MODULE,
 372        .act            =       tcf_vlan_act,
 373        .dump           =       tcf_vlan_dump,
 374        .init           =       tcf_vlan_init,
 375        .cleanup        =       tcf_vlan_cleanup,
 376        .walk           =       tcf_vlan_walker,
 377        .stats_update   =       tcf_vlan_stats_update,
 378        .get_fill_size  =       tcf_vlan_get_fill_size,
 379        .lookup         =       tcf_vlan_search,
 380        .size           =       sizeof(struct tcf_vlan),
 381};
 382
 383static __net_init int vlan_init_net(struct net *net)
 384{
 385        struct tc_action_net *tn = net_generic(net, vlan_net_id);
 386
 387        return tc_action_net_init(net, tn, &act_vlan_ops);
 388}
 389
 390static void __net_exit vlan_exit_net(struct list_head *net_list)
 391{
 392        tc_action_net_exit(net_list, vlan_net_id);
 393}
 394
 395static struct pernet_operations vlan_net_ops = {
 396        .init = vlan_init_net,
 397        .exit_batch = vlan_exit_net,
 398        .id   = &vlan_net_id,
 399        .size = sizeof(struct tc_action_net),
 400};
 401
 402static int __init vlan_init_module(void)
 403{
 404        return tcf_register_action(&act_vlan_ops, &vlan_net_ops);
 405}
 406
 407static void __exit vlan_cleanup_module(void)
 408{
 409        tcf_unregister_action(&act_vlan_ops, &vlan_net_ops);
 410}
 411
 412module_init(vlan_init_module);
 413module_exit(vlan_cleanup_module);
 414
 415MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>");
 416MODULE_DESCRIPTION("vlan manipulation actions");
 417MODULE_LICENSE("GPL v2");
 418