linux/net/sched/act_mpls.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
   2/* Copyright (C) 2019 Netronome Systems, Inc. */
   3
   4#include <linux/if_arp.h>
   5#include <linux/init.h>
   6#include <linux/kernel.h>
   7#include <linux/module.h>
   8#include <linux/mpls.h>
   9#include <linux/rtnetlink.h>
  10#include <linux/skbuff.h>
  11#include <linux/tc_act/tc_mpls.h>
  12#include <net/mpls.h>
  13#include <net/netlink.h>
  14#include <net/pkt_sched.h>
  15#include <net/pkt_cls.h>
  16#include <net/tc_act/tc_mpls.h>
  17
  18static unsigned int mpls_net_id;
  19static struct tc_action_ops act_mpls_ops;
  20
  21#define ACT_MPLS_TTL_DEFAULT    255
  22
  23static __be32 tcf_mpls_get_lse(struct mpls_shim_hdr *lse,
  24                               struct tcf_mpls_params *p, bool set_bos)
  25{
  26        u32 new_lse = 0;
  27
  28        if (lse)
  29                new_lse = be32_to_cpu(lse->label_stack_entry);
  30
  31        if (p->tcfm_label != ACT_MPLS_LABEL_NOT_SET) {
  32                new_lse &= ~MPLS_LS_LABEL_MASK;
  33                new_lse |= p->tcfm_label << MPLS_LS_LABEL_SHIFT;
  34        }
  35        if (p->tcfm_ttl) {
  36                new_lse &= ~MPLS_LS_TTL_MASK;
  37                new_lse |= p->tcfm_ttl << MPLS_LS_TTL_SHIFT;
  38        }
  39        if (p->tcfm_tc != ACT_MPLS_TC_NOT_SET) {
  40                new_lse &= ~MPLS_LS_TC_MASK;
  41                new_lse |= p->tcfm_tc << MPLS_LS_TC_SHIFT;
  42        }
  43        if (p->tcfm_bos != ACT_MPLS_BOS_NOT_SET) {
  44                new_lse &= ~MPLS_LS_S_MASK;
  45                new_lse |= p->tcfm_bos << MPLS_LS_S_SHIFT;
  46        } else if (set_bos) {
  47                new_lse |= 1 << MPLS_LS_S_SHIFT;
  48        }
  49
  50        return cpu_to_be32(new_lse);
  51}
  52
  53static int tcf_mpls_act(struct sk_buff *skb, const struct tc_action *a,
  54                        struct tcf_result *res)
  55{
  56        struct tcf_mpls *m = to_mpls(a);
  57        struct tcf_mpls_params *p;
  58        __be32 new_lse;
  59        int ret, mac_len;
  60
  61        tcf_lastuse_update(&m->tcf_tm);
  62        bstats_cpu_update(this_cpu_ptr(m->common.cpu_bstats), skb);
  63
  64        /* Ensure 'data' points at mac_header prior calling mpls manipulating
  65         * functions.
  66         */
  67        if (skb_at_tc_ingress(skb)) {
  68                skb_push_rcsum(skb, skb->mac_len);
  69                mac_len = skb->mac_len;
  70        } else {
  71                mac_len = skb_network_header(skb) - skb_mac_header(skb);
  72        }
  73
  74        ret = READ_ONCE(m->tcf_action);
  75
  76        p = rcu_dereference_bh(m->mpls_p);
  77
  78        switch (p->tcfm_action) {
  79        case TCA_MPLS_ACT_POP:
  80                if (skb_mpls_pop(skb, p->tcfm_proto, mac_len,
  81                                 skb->dev && skb->dev->type == ARPHRD_ETHER))
  82                        goto drop;
  83                break;
  84        case TCA_MPLS_ACT_PUSH:
  85                new_lse = tcf_mpls_get_lse(NULL, p, !eth_p_mpls(skb->protocol));
  86                if (skb_mpls_push(skb, new_lse, p->tcfm_proto, mac_len,
  87                                  skb->dev && skb->dev->type == ARPHRD_ETHER))
  88                        goto drop;
  89                break;
  90        case TCA_MPLS_ACT_MODIFY:
  91                new_lse = tcf_mpls_get_lse(mpls_hdr(skb), p, false);
  92                if (skb_mpls_update_lse(skb, new_lse))
  93                        goto drop;
  94                break;
  95        case TCA_MPLS_ACT_DEC_TTL:
  96                if (skb_mpls_dec_ttl(skb))
  97                        goto drop;
  98                break;
  99        }
 100
 101        if (skb_at_tc_ingress(skb))
 102                skb_pull_rcsum(skb, skb->mac_len);
 103
 104        return ret;
 105
 106drop:
 107        qstats_drop_inc(this_cpu_ptr(m->common.cpu_qstats));
 108        return TC_ACT_SHOT;
 109}
 110
 111static int valid_label(const struct nlattr *attr,
 112                       struct netlink_ext_ack *extack)
 113{
 114        const u32 *label = nla_data(attr);
 115
 116        if (*label & ~MPLS_LABEL_MASK || *label == MPLS_LABEL_IMPLNULL) {
 117                NL_SET_ERR_MSG_MOD(extack, "MPLS label out of range");
 118                return -EINVAL;
 119        }
 120
 121        return 0;
 122}
 123
 124static const struct nla_policy mpls_policy[TCA_MPLS_MAX + 1] = {
 125        [TCA_MPLS_PARMS]        = NLA_POLICY_EXACT_LEN(sizeof(struct tc_mpls)),
 126        [TCA_MPLS_PROTO]        = { .type = NLA_U16 },
 127        [TCA_MPLS_LABEL]        = NLA_POLICY_VALIDATE_FN(NLA_U32, valid_label),
 128        [TCA_MPLS_TC]           = NLA_POLICY_RANGE(NLA_U8, 0, 7),
 129        [TCA_MPLS_TTL]          = NLA_POLICY_MIN(NLA_U8, 1),
 130        [TCA_MPLS_BOS]          = NLA_POLICY_RANGE(NLA_U8, 0, 1),
 131};
 132
 133static int tcf_mpls_init(struct net *net, struct nlattr *nla,
 134                         struct nlattr *est, struct tc_action **a,
 135                         int ovr, int bind, bool rtnl_held,
 136                         struct tcf_proto *tp, u32 flags,
 137                         struct netlink_ext_ack *extack)
 138{
 139        struct tc_action_net *tn = net_generic(net, mpls_net_id);
 140        struct nlattr *tb[TCA_MPLS_MAX + 1];
 141        struct tcf_chain *goto_ch = NULL;
 142        struct tcf_mpls_params *p;
 143        struct tc_mpls *parm;
 144        bool exists = false;
 145        struct tcf_mpls *m;
 146        int ret = 0, err;
 147        u8 mpls_ttl = 0;
 148        u32 index;
 149
 150        if (!nla) {
 151                NL_SET_ERR_MSG_MOD(extack, "Missing netlink attributes");
 152                return -EINVAL;
 153        }
 154
 155        err = nla_parse_nested(tb, TCA_MPLS_MAX, nla, mpls_policy, extack);
 156        if (err < 0)
 157                return err;
 158
 159        if (!tb[TCA_MPLS_PARMS]) {
 160                NL_SET_ERR_MSG_MOD(extack, "No MPLS params");
 161                return -EINVAL;
 162        }
 163        parm = nla_data(tb[TCA_MPLS_PARMS]);
 164        index = parm->index;
 165
 166        /* Verify parameters against action type. */
 167        switch (parm->m_action) {
 168        case TCA_MPLS_ACT_POP:
 169                if (!tb[TCA_MPLS_PROTO]) {
 170                        NL_SET_ERR_MSG_MOD(extack, "Protocol must be set for MPLS pop");
 171                        return -EINVAL;
 172                }
 173                if (!eth_proto_is_802_3(nla_get_be16(tb[TCA_MPLS_PROTO]))) {
 174                        NL_SET_ERR_MSG_MOD(extack, "Invalid protocol type for MPLS pop");
 175                        return -EINVAL;
 176                }
 177                if (tb[TCA_MPLS_LABEL] || tb[TCA_MPLS_TTL] || tb[TCA_MPLS_TC] ||
 178                    tb[TCA_MPLS_BOS]) {
 179                        NL_SET_ERR_MSG_MOD(extack, "Label, TTL, TC or BOS cannot be used with MPLS pop");
 180                        return -EINVAL;
 181                }
 182                break;
 183        case TCA_MPLS_ACT_DEC_TTL:
 184                if (tb[TCA_MPLS_PROTO] || tb[TCA_MPLS_LABEL] ||
 185                    tb[TCA_MPLS_TTL] || tb[TCA_MPLS_TC] || tb[TCA_MPLS_BOS]) {
 186                        NL_SET_ERR_MSG_MOD(extack, "Label, TTL, TC, BOS or protocol cannot be used with MPLS dec_ttl");
 187                        return -EINVAL;
 188                }
 189                break;
 190        case TCA_MPLS_ACT_PUSH:
 191                if (!tb[TCA_MPLS_LABEL]) {
 192                        NL_SET_ERR_MSG_MOD(extack, "Label is required for MPLS push");
 193                        return -EINVAL;
 194                }
 195                if (tb[TCA_MPLS_PROTO] &&
 196                    !eth_p_mpls(nla_get_be16(tb[TCA_MPLS_PROTO]))) {
 197                        NL_SET_ERR_MSG_MOD(extack, "Protocol must be an MPLS type for MPLS push");
 198                        return -EPROTONOSUPPORT;
 199                }
 200                /* Push needs a TTL - if not specified, set a default value. */
 201                if (!tb[TCA_MPLS_TTL]) {
 202#if IS_ENABLED(CONFIG_MPLS)
 203                        mpls_ttl = net->mpls.default_ttl ?
 204                                   net->mpls.default_ttl : ACT_MPLS_TTL_DEFAULT;
 205#else
 206                        mpls_ttl = ACT_MPLS_TTL_DEFAULT;
 207#endif
 208                }
 209                break;
 210        case TCA_MPLS_ACT_MODIFY:
 211                if (tb[TCA_MPLS_PROTO]) {
 212                        NL_SET_ERR_MSG_MOD(extack, "Protocol cannot be used with MPLS modify");
 213                        return -EINVAL;
 214                }
 215                break;
 216        default:
 217                NL_SET_ERR_MSG_MOD(extack, "Unknown MPLS action");
 218                return -EINVAL;
 219        }
 220
 221        err = tcf_idr_check_alloc(tn, &index, a, bind);
 222        if (err < 0)
 223                return err;
 224        exists = err;
 225        if (exists && bind)
 226                return 0;
 227
 228        if (!exists) {
 229                ret = tcf_idr_create(tn, index, est, a,
 230                                     &act_mpls_ops, bind, true, 0);
 231                if (ret) {
 232                        tcf_idr_cleanup(tn, index);
 233                        return ret;
 234                }
 235
 236                ret = ACT_P_CREATED;
 237        } else if (!ovr) {
 238                tcf_idr_release(*a, bind);
 239                return -EEXIST;
 240        }
 241
 242        err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
 243        if (err < 0)
 244                goto release_idr;
 245
 246        m = to_mpls(*a);
 247
 248        p = kzalloc(sizeof(*p), GFP_KERNEL);
 249        if (!p) {
 250                err = -ENOMEM;
 251                goto put_chain;
 252        }
 253
 254        p->tcfm_action = parm->m_action;
 255        p->tcfm_label = tb[TCA_MPLS_LABEL] ? nla_get_u32(tb[TCA_MPLS_LABEL]) :
 256                                             ACT_MPLS_LABEL_NOT_SET;
 257        p->tcfm_tc = tb[TCA_MPLS_TC] ? nla_get_u8(tb[TCA_MPLS_TC]) :
 258                                       ACT_MPLS_TC_NOT_SET;
 259        p->tcfm_ttl = tb[TCA_MPLS_TTL] ? nla_get_u8(tb[TCA_MPLS_TTL]) :
 260                                         mpls_ttl;
 261        p->tcfm_bos = tb[TCA_MPLS_BOS] ? nla_get_u8(tb[TCA_MPLS_BOS]) :
 262                                         ACT_MPLS_BOS_NOT_SET;
 263        p->tcfm_proto = tb[TCA_MPLS_PROTO] ? nla_get_be16(tb[TCA_MPLS_PROTO]) :
 264                                             htons(ETH_P_MPLS_UC);
 265
 266        spin_lock_bh(&m->tcf_lock);
 267        goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
 268        p = rcu_replace_pointer(m->mpls_p, p, lockdep_is_held(&m->tcf_lock));
 269        spin_unlock_bh(&m->tcf_lock);
 270
 271        if (goto_ch)
 272                tcf_chain_put_by_act(goto_ch);
 273        if (p)
 274                kfree_rcu(p, rcu);
 275
 276        if (ret == ACT_P_CREATED)
 277                tcf_idr_insert(tn, *a);
 278        return ret;
 279put_chain:
 280        if (goto_ch)
 281                tcf_chain_put_by_act(goto_ch);
 282release_idr:
 283        tcf_idr_release(*a, bind);
 284        return err;
 285}
 286
 287static void tcf_mpls_cleanup(struct tc_action *a)
 288{
 289        struct tcf_mpls *m = to_mpls(a);
 290        struct tcf_mpls_params *p;
 291
 292        p = rcu_dereference_protected(m->mpls_p, 1);
 293        if (p)
 294                kfree_rcu(p, rcu);
 295}
 296
 297static int tcf_mpls_dump(struct sk_buff *skb, struct tc_action *a,
 298                         int bind, int ref)
 299{
 300        unsigned char *b = skb_tail_pointer(skb);
 301        struct tcf_mpls *m = to_mpls(a);
 302        struct tcf_mpls_params *p;
 303        struct tc_mpls opt = {
 304                .index    = m->tcf_index,
 305                .refcnt   = refcount_read(&m->tcf_refcnt) - ref,
 306                .bindcnt  = atomic_read(&m->tcf_bindcnt) - bind,
 307        };
 308        struct tcf_t t;
 309
 310        spin_lock_bh(&m->tcf_lock);
 311        opt.action = m->tcf_action;
 312        p = rcu_dereference_protected(m->mpls_p, lockdep_is_held(&m->tcf_lock));
 313        opt.m_action = p->tcfm_action;
 314
 315        if (nla_put(skb, TCA_MPLS_PARMS, sizeof(opt), &opt))
 316                goto nla_put_failure;
 317
 318        if (p->tcfm_label != ACT_MPLS_LABEL_NOT_SET &&
 319            nla_put_u32(skb, TCA_MPLS_LABEL, p->tcfm_label))
 320                goto nla_put_failure;
 321
 322        if (p->tcfm_tc != ACT_MPLS_TC_NOT_SET &&
 323            nla_put_u8(skb, TCA_MPLS_TC, p->tcfm_tc))
 324                goto nla_put_failure;
 325
 326        if (p->tcfm_ttl && nla_put_u8(skb, TCA_MPLS_TTL, p->tcfm_ttl))
 327                goto nla_put_failure;
 328
 329        if (p->tcfm_bos != ACT_MPLS_BOS_NOT_SET &&
 330            nla_put_u8(skb, TCA_MPLS_BOS, p->tcfm_bos))
 331                goto nla_put_failure;
 332
 333        if (nla_put_be16(skb, TCA_MPLS_PROTO, p->tcfm_proto))
 334                goto nla_put_failure;
 335
 336        tcf_tm_dump(&t, &m->tcf_tm);
 337
 338        if (nla_put_64bit(skb, TCA_MPLS_TM, sizeof(t), &t, TCA_MPLS_PAD))
 339                goto nla_put_failure;
 340
 341        spin_unlock_bh(&m->tcf_lock);
 342
 343        return skb->len;
 344
 345nla_put_failure:
 346        spin_unlock_bh(&m->tcf_lock);
 347        nlmsg_trim(skb, b);
 348        return -EMSGSIZE;
 349}
 350
 351static int tcf_mpls_walker(struct net *net, struct sk_buff *skb,
 352                           struct netlink_callback *cb, int type,
 353                           const struct tc_action_ops *ops,
 354                           struct netlink_ext_ack *extack)
 355{
 356        struct tc_action_net *tn = net_generic(net, mpls_net_id);
 357
 358        return tcf_generic_walker(tn, skb, cb, type, ops, extack);
 359}
 360
 361static int tcf_mpls_search(struct net *net, struct tc_action **a, u32 index)
 362{
 363        struct tc_action_net *tn = net_generic(net, mpls_net_id);
 364
 365        return tcf_idr_search(tn, a, index);
 366}
 367
 368static struct tc_action_ops act_mpls_ops = {
 369        .kind           =       "mpls",
 370        .id             =       TCA_ID_MPLS,
 371        .owner          =       THIS_MODULE,
 372        .act            =       tcf_mpls_act,
 373        .dump           =       tcf_mpls_dump,
 374        .init           =       tcf_mpls_init,
 375        .cleanup        =       tcf_mpls_cleanup,
 376        .walk           =       tcf_mpls_walker,
 377        .lookup         =       tcf_mpls_search,
 378        .size           =       sizeof(struct tcf_mpls),
 379};
 380
 381static __net_init int mpls_init_net(struct net *net)
 382{
 383        struct tc_action_net *tn = net_generic(net, mpls_net_id);
 384
 385        return tc_action_net_init(net, tn, &act_mpls_ops);
 386}
 387
 388static void __net_exit mpls_exit_net(struct list_head *net_list)
 389{
 390        tc_action_net_exit(net_list, mpls_net_id);
 391}
 392
 393static struct pernet_operations mpls_net_ops = {
 394        .init = mpls_init_net,
 395        .exit_batch = mpls_exit_net,
 396        .id   = &mpls_net_id,
 397        .size = sizeof(struct tc_action_net),
 398};
 399
 400static int __init mpls_init_module(void)
 401{
 402        return tcf_register_action(&act_mpls_ops, &mpls_net_ops);
 403}
 404
 405static void __exit mpls_cleanup_module(void)
 406{
 407        tcf_unregister_action(&act_mpls_ops, &mpls_net_ops);
 408}
 409
 410module_init(mpls_init_module);
 411module_exit(mpls_cleanup_module);
 412
 413MODULE_AUTHOR("Netronome Systems <oss-drivers@netronome.com>");
 414MODULE_LICENSE("GPL");
 415MODULE_DESCRIPTION("MPLS manipulation actions");
 416