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