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