162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 262306a36Sopenharmony_ci/* Copyright (C) 2019 Netronome Systems, Inc. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/if_arp.h> 562306a36Sopenharmony_ci#include <linux/init.h> 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/mpls.h> 962306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1062306a36Sopenharmony_ci#include <linux/skbuff.h> 1162306a36Sopenharmony_ci#include <linux/tc_act/tc_mpls.h> 1262306a36Sopenharmony_ci#include <net/mpls.h> 1362306a36Sopenharmony_ci#include <net/netlink.h> 1462306a36Sopenharmony_ci#include <net/pkt_sched.h> 1562306a36Sopenharmony_ci#include <net/pkt_cls.h> 1662306a36Sopenharmony_ci#include <net/tc_act/tc_mpls.h> 1762306a36Sopenharmony_ci#include <net/tc_wrapper.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic struct tc_action_ops act_mpls_ops; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define ACT_MPLS_TTL_DEFAULT 255 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic __be32 tcf_mpls_get_lse(struct mpls_shim_hdr *lse, 2462306a36Sopenharmony_ci struct tcf_mpls_params *p, bool set_bos) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci u32 new_lse = 0; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (lse) 2962306a36Sopenharmony_ci new_lse = be32_to_cpu(lse->label_stack_entry); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci if (p->tcfm_label != ACT_MPLS_LABEL_NOT_SET) { 3262306a36Sopenharmony_ci new_lse &= ~MPLS_LS_LABEL_MASK; 3362306a36Sopenharmony_ci new_lse |= p->tcfm_label << MPLS_LS_LABEL_SHIFT; 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci if (p->tcfm_ttl) { 3662306a36Sopenharmony_ci new_lse &= ~MPLS_LS_TTL_MASK; 3762306a36Sopenharmony_ci new_lse |= p->tcfm_ttl << MPLS_LS_TTL_SHIFT; 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci if (p->tcfm_tc != ACT_MPLS_TC_NOT_SET) { 4062306a36Sopenharmony_ci new_lse &= ~MPLS_LS_TC_MASK; 4162306a36Sopenharmony_ci new_lse |= p->tcfm_tc << MPLS_LS_TC_SHIFT; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci if (p->tcfm_bos != ACT_MPLS_BOS_NOT_SET) { 4462306a36Sopenharmony_ci new_lse &= ~MPLS_LS_S_MASK; 4562306a36Sopenharmony_ci new_lse |= p->tcfm_bos << MPLS_LS_S_SHIFT; 4662306a36Sopenharmony_ci } else if (set_bos) { 4762306a36Sopenharmony_ci new_lse |= 1 << MPLS_LS_S_SHIFT; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return cpu_to_be32(new_lse); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ciTC_INDIRECT_SCOPE int tcf_mpls_act(struct sk_buff *skb, 5462306a36Sopenharmony_ci const struct tc_action *a, 5562306a36Sopenharmony_ci struct tcf_result *res) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct tcf_mpls *m = to_mpls(a); 5862306a36Sopenharmony_ci struct tcf_mpls_params *p; 5962306a36Sopenharmony_ci __be32 new_lse; 6062306a36Sopenharmony_ci int ret, mac_len; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci tcf_lastuse_update(&m->tcf_tm); 6362306a36Sopenharmony_ci bstats_update(this_cpu_ptr(m->common.cpu_bstats), skb); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* Ensure 'data' points at mac_header prior calling mpls manipulating 6662306a36Sopenharmony_ci * functions. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci if (skb_at_tc_ingress(skb)) { 6962306a36Sopenharmony_ci skb_push_rcsum(skb, skb->mac_len); 7062306a36Sopenharmony_ci mac_len = skb->mac_len; 7162306a36Sopenharmony_ci } else { 7262306a36Sopenharmony_ci mac_len = skb_network_offset(skb); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci ret = READ_ONCE(m->tcf_action); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci p = rcu_dereference_bh(m->mpls_p); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci switch (p->tcfm_action) { 8062306a36Sopenharmony_ci case TCA_MPLS_ACT_POP: 8162306a36Sopenharmony_ci if (skb_mpls_pop(skb, p->tcfm_proto, mac_len, 8262306a36Sopenharmony_ci skb->dev && skb->dev->type == ARPHRD_ETHER)) 8362306a36Sopenharmony_ci goto drop; 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci case TCA_MPLS_ACT_PUSH: 8662306a36Sopenharmony_ci new_lse = tcf_mpls_get_lse(NULL, p, !eth_p_mpls(skb_protocol(skb, true))); 8762306a36Sopenharmony_ci if (skb_mpls_push(skb, new_lse, p->tcfm_proto, mac_len, 8862306a36Sopenharmony_ci skb->dev && skb->dev->type == ARPHRD_ETHER)) 8962306a36Sopenharmony_ci goto drop; 9062306a36Sopenharmony_ci break; 9162306a36Sopenharmony_ci case TCA_MPLS_ACT_MAC_PUSH: 9262306a36Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 9362306a36Sopenharmony_ci if (__vlan_insert_inner_tag(skb, skb->vlan_proto, 9462306a36Sopenharmony_ci skb_vlan_tag_get(skb), 9562306a36Sopenharmony_ci ETH_HLEN) < 0) 9662306a36Sopenharmony_ci goto drop; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci skb->protocol = skb->vlan_proto; 9962306a36Sopenharmony_ci __vlan_hwaccel_clear_tag(skb); 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci new_lse = tcf_mpls_get_lse(NULL, p, mac_len || 10362306a36Sopenharmony_ci !eth_p_mpls(skb->protocol)); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (skb_mpls_push(skb, new_lse, p->tcfm_proto, 0, false)) 10662306a36Sopenharmony_ci goto drop; 10762306a36Sopenharmony_ci break; 10862306a36Sopenharmony_ci case TCA_MPLS_ACT_MODIFY: 10962306a36Sopenharmony_ci if (!pskb_may_pull(skb, 11062306a36Sopenharmony_ci skb_network_offset(skb) + MPLS_HLEN)) 11162306a36Sopenharmony_ci goto drop; 11262306a36Sopenharmony_ci new_lse = tcf_mpls_get_lse(mpls_hdr(skb), p, false); 11362306a36Sopenharmony_ci if (skb_mpls_update_lse(skb, new_lse)) 11462306a36Sopenharmony_ci goto drop; 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci case TCA_MPLS_ACT_DEC_TTL: 11762306a36Sopenharmony_ci if (skb_mpls_dec_ttl(skb)) 11862306a36Sopenharmony_ci goto drop; 11962306a36Sopenharmony_ci break; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (skb_at_tc_ingress(skb)) 12362306a36Sopenharmony_ci skb_pull_rcsum(skb, skb->mac_len); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return ret; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cidrop: 12862306a36Sopenharmony_ci qstats_drop_inc(this_cpu_ptr(m->common.cpu_qstats)); 12962306a36Sopenharmony_ci return TC_ACT_SHOT; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic int valid_label(const struct nlattr *attr, 13362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci const u32 *label = nla_data(attr); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (nla_len(attr) != sizeof(*label)) { 13862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid MPLS label length"); 13962306a36Sopenharmony_ci return -EINVAL; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (*label & ~MPLS_LABEL_MASK || *label == MPLS_LABEL_IMPLNULL) { 14362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "MPLS label out of range"); 14462306a36Sopenharmony_ci return -EINVAL; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic const struct nla_policy mpls_policy[TCA_MPLS_MAX + 1] = { 15162306a36Sopenharmony_ci [TCA_MPLS_PARMS] = NLA_POLICY_EXACT_LEN(sizeof(struct tc_mpls)), 15262306a36Sopenharmony_ci [TCA_MPLS_PROTO] = { .type = NLA_U16 }, 15362306a36Sopenharmony_ci [TCA_MPLS_LABEL] = NLA_POLICY_VALIDATE_FN(NLA_BINARY, 15462306a36Sopenharmony_ci valid_label), 15562306a36Sopenharmony_ci [TCA_MPLS_TC] = NLA_POLICY_RANGE(NLA_U8, 0, 7), 15662306a36Sopenharmony_ci [TCA_MPLS_TTL] = NLA_POLICY_MIN(NLA_U8, 1), 15762306a36Sopenharmony_ci [TCA_MPLS_BOS] = NLA_POLICY_RANGE(NLA_U8, 0, 1), 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int tcf_mpls_init(struct net *net, struct nlattr *nla, 16162306a36Sopenharmony_ci struct nlattr *est, struct tc_action **a, 16262306a36Sopenharmony_ci struct tcf_proto *tp, u32 flags, 16362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct tc_action_net *tn = net_generic(net, act_mpls_ops.net_id); 16662306a36Sopenharmony_ci bool bind = flags & TCA_ACT_FLAGS_BIND; 16762306a36Sopenharmony_ci struct nlattr *tb[TCA_MPLS_MAX + 1]; 16862306a36Sopenharmony_ci struct tcf_chain *goto_ch = NULL; 16962306a36Sopenharmony_ci struct tcf_mpls_params *p; 17062306a36Sopenharmony_ci struct tc_mpls *parm; 17162306a36Sopenharmony_ci bool exists = false; 17262306a36Sopenharmony_ci struct tcf_mpls *m; 17362306a36Sopenharmony_ci int ret = 0, err; 17462306a36Sopenharmony_ci u8 mpls_ttl = 0; 17562306a36Sopenharmony_ci u32 index; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (!nla) { 17862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Missing netlink attributes"); 17962306a36Sopenharmony_ci return -EINVAL; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci err = nla_parse_nested(tb, TCA_MPLS_MAX, nla, mpls_policy, extack); 18362306a36Sopenharmony_ci if (err < 0) 18462306a36Sopenharmony_ci return err; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (!tb[TCA_MPLS_PARMS]) { 18762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "No MPLS params"); 18862306a36Sopenharmony_ci return -EINVAL; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci parm = nla_data(tb[TCA_MPLS_PARMS]); 19162306a36Sopenharmony_ci index = parm->index; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci err = tcf_idr_check_alloc(tn, &index, a, bind); 19462306a36Sopenharmony_ci if (err < 0) 19562306a36Sopenharmony_ci return err; 19662306a36Sopenharmony_ci exists = err; 19762306a36Sopenharmony_ci if (exists && bind) 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (!exists) { 20162306a36Sopenharmony_ci ret = tcf_idr_create(tn, index, est, a, &act_mpls_ops, bind, 20262306a36Sopenharmony_ci true, flags); 20362306a36Sopenharmony_ci if (ret) { 20462306a36Sopenharmony_ci tcf_idr_cleanup(tn, index); 20562306a36Sopenharmony_ci return ret; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci ret = ACT_P_CREATED; 20962306a36Sopenharmony_ci } else if (!(flags & TCA_ACT_FLAGS_REPLACE)) { 21062306a36Sopenharmony_ci tcf_idr_release(*a, bind); 21162306a36Sopenharmony_ci return -EEXIST; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* Verify parameters against action type. */ 21562306a36Sopenharmony_ci switch (parm->m_action) { 21662306a36Sopenharmony_ci case TCA_MPLS_ACT_POP: 21762306a36Sopenharmony_ci if (!tb[TCA_MPLS_PROTO]) { 21862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Protocol must be set for MPLS pop"); 21962306a36Sopenharmony_ci err = -EINVAL; 22062306a36Sopenharmony_ci goto release_idr; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci if (!eth_proto_is_802_3(nla_get_be16(tb[TCA_MPLS_PROTO]))) { 22362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid protocol type for MPLS pop"); 22462306a36Sopenharmony_ci err = -EINVAL; 22562306a36Sopenharmony_ci goto release_idr; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci if (tb[TCA_MPLS_LABEL] || tb[TCA_MPLS_TTL] || tb[TCA_MPLS_TC] || 22862306a36Sopenharmony_ci tb[TCA_MPLS_BOS]) { 22962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Label, TTL, TC or BOS cannot be used with MPLS pop"); 23062306a36Sopenharmony_ci err = -EINVAL; 23162306a36Sopenharmony_ci goto release_idr; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci case TCA_MPLS_ACT_DEC_TTL: 23562306a36Sopenharmony_ci if (tb[TCA_MPLS_PROTO] || tb[TCA_MPLS_LABEL] || 23662306a36Sopenharmony_ci tb[TCA_MPLS_TTL] || tb[TCA_MPLS_TC] || tb[TCA_MPLS_BOS]) { 23762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Label, TTL, TC, BOS or protocol cannot be used with MPLS dec_ttl"); 23862306a36Sopenharmony_ci err = -EINVAL; 23962306a36Sopenharmony_ci goto release_idr; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci case TCA_MPLS_ACT_PUSH: 24362306a36Sopenharmony_ci case TCA_MPLS_ACT_MAC_PUSH: 24462306a36Sopenharmony_ci if (!tb[TCA_MPLS_LABEL]) { 24562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Label is required for MPLS push"); 24662306a36Sopenharmony_ci err = -EINVAL; 24762306a36Sopenharmony_ci goto release_idr; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci if (tb[TCA_MPLS_PROTO] && 25062306a36Sopenharmony_ci !eth_p_mpls(nla_get_be16(tb[TCA_MPLS_PROTO]))) { 25162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Protocol must be an MPLS type for MPLS push"); 25262306a36Sopenharmony_ci err = -EPROTONOSUPPORT; 25362306a36Sopenharmony_ci goto release_idr; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci /* Push needs a TTL - if not specified, set a default value. */ 25662306a36Sopenharmony_ci if (!tb[TCA_MPLS_TTL]) { 25762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_MPLS) 25862306a36Sopenharmony_ci mpls_ttl = net->mpls.default_ttl ? 25962306a36Sopenharmony_ci net->mpls.default_ttl : ACT_MPLS_TTL_DEFAULT; 26062306a36Sopenharmony_ci#else 26162306a36Sopenharmony_ci mpls_ttl = ACT_MPLS_TTL_DEFAULT; 26262306a36Sopenharmony_ci#endif 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci case TCA_MPLS_ACT_MODIFY: 26662306a36Sopenharmony_ci if (tb[TCA_MPLS_PROTO]) { 26762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Protocol cannot be used with MPLS modify"); 26862306a36Sopenharmony_ci err = -EINVAL; 26962306a36Sopenharmony_ci goto release_idr; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci default: 27362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unknown MPLS action"); 27462306a36Sopenharmony_ci err = -EINVAL; 27562306a36Sopenharmony_ci goto release_idr; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); 27962306a36Sopenharmony_ci if (err < 0) 28062306a36Sopenharmony_ci goto release_idr; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci m = to_mpls(*a); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci p = kzalloc(sizeof(*p), GFP_KERNEL); 28562306a36Sopenharmony_ci if (!p) { 28662306a36Sopenharmony_ci err = -ENOMEM; 28762306a36Sopenharmony_ci goto put_chain; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci p->tcfm_action = parm->m_action; 29162306a36Sopenharmony_ci p->tcfm_label = tb[TCA_MPLS_LABEL] ? nla_get_u32(tb[TCA_MPLS_LABEL]) : 29262306a36Sopenharmony_ci ACT_MPLS_LABEL_NOT_SET; 29362306a36Sopenharmony_ci p->tcfm_tc = tb[TCA_MPLS_TC] ? nla_get_u8(tb[TCA_MPLS_TC]) : 29462306a36Sopenharmony_ci ACT_MPLS_TC_NOT_SET; 29562306a36Sopenharmony_ci p->tcfm_ttl = tb[TCA_MPLS_TTL] ? nla_get_u8(tb[TCA_MPLS_TTL]) : 29662306a36Sopenharmony_ci mpls_ttl; 29762306a36Sopenharmony_ci p->tcfm_bos = tb[TCA_MPLS_BOS] ? nla_get_u8(tb[TCA_MPLS_BOS]) : 29862306a36Sopenharmony_ci ACT_MPLS_BOS_NOT_SET; 29962306a36Sopenharmony_ci p->tcfm_proto = tb[TCA_MPLS_PROTO] ? nla_get_be16(tb[TCA_MPLS_PROTO]) : 30062306a36Sopenharmony_ci htons(ETH_P_MPLS_UC); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci spin_lock_bh(&m->tcf_lock); 30362306a36Sopenharmony_ci goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); 30462306a36Sopenharmony_ci p = rcu_replace_pointer(m->mpls_p, p, lockdep_is_held(&m->tcf_lock)); 30562306a36Sopenharmony_ci spin_unlock_bh(&m->tcf_lock); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (goto_ch) 30862306a36Sopenharmony_ci tcf_chain_put_by_act(goto_ch); 30962306a36Sopenharmony_ci if (p) 31062306a36Sopenharmony_ci kfree_rcu(p, rcu); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return ret; 31362306a36Sopenharmony_ciput_chain: 31462306a36Sopenharmony_ci if (goto_ch) 31562306a36Sopenharmony_ci tcf_chain_put_by_act(goto_ch); 31662306a36Sopenharmony_cirelease_idr: 31762306a36Sopenharmony_ci tcf_idr_release(*a, bind); 31862306a36Sopenharmony_ci return err; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic void tcf_mpls_cleanup(struct tc_action *a) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct tcf_mpls *m = to_mpls(a); 32462306a36Sopenharmony_ci struct tcf_mpls_params *p; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci p = rcu_dereference_protected(m->mpls_p, 1); 32762306a36Sopenharmony_ci if (p) 32862306a36Sopenharmony_ci kfree_rcu(p, rcu); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic int tcf_mpls_dump(struct sk_buff *skb, struct tc_action *a, 33262306a36Sopenharmony_ci int bind, int ref) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 33562306a36Sopenharmony_ci struct tcf_mpls *m = to_mpls(a); 33662306a36Sopenharmony_ci struct tcf_mpls_params *p; 33762306a36Sopenharmony_ci struct tc_mpls opt = { 33862306a36Sopenharmony_ci .index = m->tcf_index, 33962306a36Sopenharmony_ci .refcnt = refcount_read(&m->tcf_refcnt) - ref, 34062306a36Sopenharmony_ci .bindcnt = atomic_read(&m->tcf_bindcnt) - bind, 34162306a36Sopenharmony_ci }; 34262306a36Sopenharmony_ci struct tcf_t t; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci spin_lock_bh(&m->tcf_lock); 34562306a36Sopenharmony_ci opt.action = m->tcf_action; 34662306a36Sopenharmony_ci p = rcu_dereference_protected(m->mpls_p, lockdep_is_held(&m->tcf_lock)); 34762306a36Sopenharmony_ci opt.m_action = p->tcfm_action; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (nla_put(skb, TCA_MPLS_PARMS, sizeof(opt), &opt)) 35062306a36Sopenharmony_ci goto nla_put_failure; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (p->tcfm_label != ACT_MPLS_LABEL_NOT_SET && 35362306a36Sopenharmony_ci nla_put_u32(skb, TCA_MPLS_LABEL, p->tcfm_label)) 35462306a36Sopenharmony_ci goto nla_put_failure; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (p->tcfm_tc != ACT_MPLS_TC_NOT_SET && 35762306a36Sopenharmony_ci nla_put_u8(skb, TCA_MPLS_TC, p->tcfm_tc)) 35862306a36Sopenharmony_ci goto nla_put_failure; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (p->tcfm_ttl && nla_put_u8(skb, TCA_MPLS_TTL, p->tcfm_ttl)) 36162306a36Sopenharmony_ci goto nla_put_failure; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (p->tcfm_bos != ACT_MPLS_BOS_NOT_SET && 36462306a36Sopenharmony_ci nla_put_u8(skb, TCA_MPLS_BOS, p->tcfm_bos)) 36562306a36Sopenharmony_ci goto nla_put_failure; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (nla_put_be16(skb, TCA_MPLS_PROTO, p->tcfm_proto)) 36862306a36Sopenharmony_ci goto nla_put_failure; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci tcf_tm_dump(&t, &m->tcf_tm); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (nla_put_64bit(skb, TCA_MPLS_TM, sizeof(t), &t, TCA_MPLS_PAD)) 37362306a36Sopenharmony_ci goto nla_put_failure; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci spin_unlock_bh(&m->tcf_lock); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return skb->len; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cinla_put_failure: 38062306a36Sopenharmony_ci spin_unlock_bh(&m->tcf_lock); 38162306a36Sopenharmony_ci nlmsg_trim(skb, b); 38262306a36Sopenharmony_ci return -EMSGSIZE; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic int tcf_mpls_offload_act_setup(struct tc_action *act, void *entry_data, 38662306a36Sopenharmony_ci u32 *index_inc, bool bind, 38762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci if (bind) { 39062306a36Sopenharmony_ci struct flow_action_entry *entry = entry_data; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci switch (tcf_mpls_action(act)) { 39362306a36Sopenharmony_ci case TCA_MPLS_ACT_PUSH: 39462306a36Sopenharmony_ci entry->id = FLOW_ACTION_MPLS_PUSH; 39562306a36Sopenharmony_ci entry->mpls_push.proto = tcf_mpls_proto(act); 39662306a36Sopenharmony_ci entry->mpls_push.label = tcf_mpls_label(act); 39762306a36Sopenharmony_ci entry->mpls_push.tc = tcf_mpls_tc(act); 39862306a36Sopenharmony_ci entry->mpls_push.bos = tcf_mpls_bos(act); 39962306a36Sopenharmony_ci entry->mpls_push.ttl = tcf_mpls_ttl(act); 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci case TCA_MPLS_ACT_POP: 40262306a36Sopenharmony_ci entry->id = FLOW_ACTION_MPLS_POP; 40362306a36Sopenharmony_ci entry->mpls_pop.proto = tcf_mpls_proto(act); 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci case TCA_MPLS_ACT_MODIFY: 40662306a36Sopenharmony_ci entry->id = FLOW_ACTION_MPLS_MANGLE; 40762306a36Sopenharmony_ci entry->mpls_mangle.label = tcf_mpls_label(act); 40862306a36Sopenharmony_ci entry->mpls_mangle.tc = tcf_mpls_tc(act); 40962306a36Sopenharmony_ci entry->mpls_mangle.bos = tcf_mpls_bos(act); 41062306a36Sopenharmony_ci entry->mpls_mangle.ttl = tcf_mpls_ttl(act); 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci case TCA_MPLS_ACT_DEC_TTL: 41362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"dec_ttl\" option is used"); 41462306a36Sopenharmony_ci return -EOPNOTSUPP; 41562306a36Sopenharmony_ci case TCA_MPLS_ACT_MAC_PUSH: 41662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Offload not supported when \"mac_push\" option is used"); 41762306a36Sopenharmony_ci return -EOPNOTSUPP; 41862306a36Sopenharmony_ci default: 41962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported MPLS mode offload"); 42062306a36Sopenharmony_ci return -EOPNOTSUPP; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci *index_inc = 1; 42362306a36Sopenharmony_ci } else { 42462306a36Sopenharmony_ci struct flow_offload_action *fl_action = entry_data; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci switch (tcf_mpls_action(act)) { 42762306a36Sopenharmony_ci case TCA_MPLS_ACT_PUSH: 42862306a36Sopenharmony_ci fl_action->id = FLOW_ACTION_MPLS_PUSH; 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci case TCA_MPLS_ACT_POP: 43162306a36Sopenharmony_ci fl_action->id = FLOW_ACTION_MPLS_POP; 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci case TCA_MPLS_ACT_MODIFY: 43462306a36Sopenharmony_ci fl_action->id = FLOW_ACTION_MPLS_MANGLE; 43562306a36Sopenharmony_ci break; 43662306a36Sopenharmony_ci default: 43762306a36Sopenharmony_ci return -EOPNOTSUPP; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic struct tc_action_ops act_mpls_ops = { 44562306a36Sopenharmony_ci .kind = "mpls", 44662306a36Sopenharmony_ci .id = TCA_ID_MPLS, 44762306a36Sopenharmony_ci .owner = THIS_MODULE, 44862306a36Sopenharmony_ci .act = tcf_mpls_act, 44962306a36Sopenharmony_ci .dump = tcf_mpls_dump, 45062306a36Sopenharmony_ci .init = tcf_mpls_init, 45162306a36Sopenharmony_ci .cleanup = tcf_mpls_cleanup, 45262306a36Sopenharmony_ci .offload_act_setup = tcf_mpls_offload_act_setup, 45362306a36Sopenharmony_ci .size = sizeof(struct tcf_mpls), 45462306a36Sopenharmony_ci}; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic __net_init int mpls_init_net(struct net *net) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct tc_action_net *tn = net_generic(net, act_mpls_ops.net_id); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return tc_action_net_init(net, tn, &act_mpls_ops); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic void __net_exit mpls_exit_net(struct list_head *net_list) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci tc_action_net_exit(net_list, act_mpls_ops.net_id); 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic struct pernet_operations mpls_net_ops = { 46962306a36Sopenharmony_ci .init = mpls_init_net, 47062306a36Sopenharmony_ci .exit_batch = mpls_exit_net, 47162306a36Sopenharmony_ci .id = &act_mpls_ops.net_id, 47262306a36Sopenharmony_ci .size = sizeof(struct tc_action_net), 47362306a36Sopenharmony_ci}; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int __init mpls_init_module(void) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci return tcf_register_action(&act_mpls_ops, &mpls_net_ops); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic void __exit mpls_cleanup_module(void) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci tcf_unregister_action(&act_mpls_ops, &mpls_net_ops); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cimodule_init(mpls_init_module); 48662306a36Sopenharmony_cimodule_exit(mpls_cleanup_module); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ciMODULE_SOFTDEP("post: mpls_gso"); 48962306a36Sopenharmony_ciMODULE_AUTHOR("Netronome Systems <oss-drivers@netronome.com>"); 49062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 49162306a36Sopenharmony_ciMODULE_DESCRIPTION("MPLS manipulation actions"); 492