Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * net/sched/act_skbmod.c  skb data modifier
0004  *
0005  * Copyright (c) 2016 Jamal Hadi Salim <jhs@mojatatu.com>
0006 */
0007 
0008 #include <linux/module.h>
0009 #include <linux/if_arp.h>
0010 #include <linux/init.h>
0011 #include <linux/kernel.h>
0012 #include <linux/skbuff.h>
0013 #include <linux/rtnetlink.h>
0014 #include <net/inet_ecn.h>
0015 #include <net/netlink.h>
0016 #include <net/pkt_sched.h>
0017 #include <net/pkt_cls.h>
0018 
0019 #include <linux/tc_act/tc_skbmod.h>
0020 #include <net/tc_act/tc_skbmod.h>
0021 
0022 static unsigned int skbmod_net_id;
0023 static struct tc_action_ops act_skbmod_ops;
0024 
0025 static int tcf_skbmod_act(struct sk_buff *skb, const struct tc_action *a,
0026               struct tcf_result *res)
0027 {
0028     struct tcf_skbmod *d = to_skbmod(a);
0029     int action, max_edit_len, err;
0030     struct tcf_skbmod_params *p;
0031     u64 flags;
0032 
0033     tcf_lastuse_update(&d->tcf_tm);
0034     bstats_update(this_cpu_ptr(d->common.cpu_bstats), skb);
0035 
0036     action = READ_ONCE(d->tcf_action);
0037     if (unlikely(action == TC_ACT_SHOT))
0038         goto drop;
0039 
0040     max_edit_len = skb_mac_header_len(skb);
0041     p = rcu_dereference_bh(d->skbmod_p);
0042     flags = p->flags;
0043 
0044     /* tcf_skbmod_init() guarantees "flags" to be one of the following:
0045      *  1. a combination of SKBMOD_F_{DMAC,SMAC,ETYPE}
0046      *  2. SKBMOD_F_SWAPMAC
0047      *  3. SKBMOD_F_ECN
0048      * SKBMOD_F_ECN only works with IP packets; all other flags only work with Ethernet
0049      * packets.
0050      */
0051     if (flags == SKBMOD_F_ECN) {
0052         switch (skb_protocol(skb, true)) {
0053         case cpu_to_be16(ETH_P_IP):
0054         case cpu_to_be16(ETH_P_IPV6):
0055             max_edit_len += skb_network_header_len(skb);
0056             break;
0057         default:
0058             goto out;
0059         }
0060     } else if (!skb->dev || skb->dev->type != ARPHRD_ETHER) {
0061         goto out;
0062     }
0063 
0064     err = skb_ensure_writable(skb, max_edit_len);
0065     if (unlikely(err)) /* best policy is to drop on the floor */
0066         goto drop;
0067 
0068     if (flags & SKBMOD_F_DMAC)
0069         ether_addr_copy(eth_hdr(skb)->h_dest, p->eth_dst);
0070     if (flags & SKBMOD_F_SMAC)
0071         ether_addr_copy(eth_hdr(skb)->h_source, p->eth_src);
0072     if (flags & SKBMOD_F_ETYPE)
0073         eth_hdr(skb)->h_proto = p->eth_type;
0074 
0075     if (flags & SKBMOD_F_SWAPMAC) {
0076         u16 tmpaddr[ETH_ALEN / 2]; /* ether_addr_copy() requirement */
0077         /*XXX: I am sure we can come up with more efficient swapping*/
0078         ether_addr_copy((u8 *)tmpaddr, eth_hdr(skb)->h_dest);
0079         ether_addr_copy(eth_hdr(skb)->h_dest, eth_hdr(skb)->h_source);
0080         ether_addr_copy(eth_hdr(skb)->h_source, (u8 *)tmpaddr);
0081     }
0082 
0083     if (flags & SKBMOD_F_ECN)
0084         INET_ECN_set_ce(skb);
0085 
0086 out:
0087     return action;
0088 
0089 drop:
0090     qstats_overlimit_inc(this_cpu_ptr(d->common.cpu_qstats));
0091     return TC_ACT_SHOT;
0092 }
0093 
0094 static const struct nla_policy skbmod_policy[TCA_SKBMOD_MAX + 1] = {
0095     [TCA_SKBMOD_PARMS]      = { .len = sizeof(struct tc_skbmod) },
0096     [TCA_SKBMOD_DMAC]       = { .len = ETH_ALEN },
0097     [TCA_SKBMOD_SMAC]       = { .len = ETH_ALEN },
0098     [TCA_SKBMOD_ETYPE]      = { .type = NLA_U16 },
0099 };
0100 
0101 static int tcf_skbmod_init(struct net *net, struct nlattr *nla,
0102                struct nlattr *est, struct tc_action **a,
0103                struct tcf_proto *tp, u32 flags,
0104                struct netlink_ext_ack *extack)
0105 {
0106     struct tc_action_net *tn = net_generic(net, skbmod_net_id);
0107     bool ovr = flags & TCA_ACT_FLAGS_REPLACE;
0108     bool bind = flags & TCA_ACT_FLAGS_BIND;
0109     struct nlattr *tb[TCA_SKBMOD_MAX + 1];
0110     struct tcf_skbmod_params *p, *p_old;
0111     struct tcf_chain *goto_ch = NULL;
0112     struct tc_skbmod *parm;
0113     u32 lflags = 0, index;
0114     struct tcf_skbmod *d;
0115     bool exists = false;
0116     u8 *daddr = NULL;
0117     u8 *saddr = NULL;
0118     u16 eth_type = 0;
0119     int ret = 0, err;
0120 
0121     if (!nla)
0122         return -EINVAL;
0123 
0124     err = nla_parse_nested_deprecated(tb, TCA_SKBMOD_MAX, nla,
0125                       skbmod_policy, NULL);
0126     if (err < 0)
0127         return err;
0128 
0129     if (!tb[TCA_SKBMOD_PARMS])
0130         return -EINVAL;
0131 
0132     if (tb[TCA_SKBMOD_DMAC]) {
0133         daddr = nla_data(tb[TCA_SKBMOD_DMAC]);
0134         lflags |= SKBMOD_F_DMAC;
0135     }
0136 
0137     if (tb[TCA_SKBMOD_SMAC]) {
0138         saddr = nla_data(tb[TCA_SKBMOD_SMAC]);
0139         lflags |= SKBMOD_F_SMAC;
0140     }
0141 
0142     if (tb[TCA_SKBMOD_ETYPE]) {
0143         eth_type = nla_get_u16(tb[TCA_SKBMOD_ETYPE]);
0144         lflags |= SKBMOD_F_ETYPE;
0145     }
0146 
0147     parm = nla_data(tb[TCA_SKBMOD_PARMS]);
0148     index = parm->index;
0149     if (parm->flags & SKBMOD_F_SWAPMAC)
0150         lflags = SKBMOD_F_SWAPMAC;
0151     if (parm->flags & SKBMOD_F_ECN)
0152         lflags = SKBMOD_F_ECN;
0153 
0154     err = tcf_idr_check_alloc(tn, &index, a, bind);
0155     if (err < 0)
0156         return err;
0157     exists = err;
0158     if (exists && bind)
0159         return 0;
0160 
0161     if (!lflags) {
0162         if (exists)
0163             tcf_idr_release(*a, bind);
0164         else
0165             tcf_idr_cleanup(tn, index);
0166         return -EINVAL;
0167     }
0168 
0169     if (!exists) {
0170         ret = tcf_idr_create(tn, index, est, a,
0171                      &act_skbmod_ops, bind, true, flags);
0172         if (ret) {
0173             tcf_idr_cleanup(tn, index);
0174             return ret;
0175         }
0176 
0177         ret = ACT_P_CREATED;
0178     } else if (!ovr) {
0179         tcf_idr_release(*a, bind);
0180         return -EEXIST;
0181     }
0182     err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
0183     if (err < 0)
0184         goto release_idr;
0185 
0186     d = to_skbmod(*a);
0187 
0188     p = kzalloc(sizeof(struct tcf_skbmod_params), GFP_KERNEL);
0189     if (unlikely(!p)) {
0190         err = -ENOMEM;
0191         goto put_chain;
0192     }
0193 
0194     p->flags = lflags;
0195 
0196     if (ovr)
0197         spin_lock_bh(&d->tcf_lock);
0198     /* Protected by tcf_lock if overwriting existing action. */
0199     goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
0200     p_old = rcu_dereference_protected(d->skbmod_p, 1);
0201 
0202     if (lflags & SKBMOD_F_DMAC)
0203         ether_addr_copy(p->eth_dst, daddr);
0204     if (lflags & SKBMOD_F_SMAC)
0205         ether_addr_copy(p->eth_src, saddr);
0206     if (lflags & SKBMOD_F_ETYPE)
0207         p->eth_type = htons(eth_type);
0208 
0209     rcu_assign_pointer(d->skbmod_p, p);
0210     if (ovr)
0211         spin_unlock_bh(&d->tcf_lock);
0212 
0213     if (p_old)
0214         kfree_rcu(p_old, rcu);
0215     if (goto_ch)
0216         tcf_chain_put_by_act(goto_ch);
0217 
0218     return ret;
0219 put_chain:
0220     if (goto_ch)
0221         tcf_chain_put_by_act(goto_ch);
0222 release_idr:
0223     tcf_idr_release(*a, bind);
0224     return err;
0225 }
0226 
0227 static void tcf_skbmod_cleanup(struct tc_action *a)
0228 {
0229     struct tcf_skbmod *d = to_skbmod(a);
0230     struct tcf_skbmod_params  *p;
0231 
0232     p = rcu_dereference_protected(d->skbmod_p, 1);
0233     if (p)
0234         kfree_rcu(p, rcu);
0235 }
0236 
0237 static int tcf_skbmod_dump(struct sk_buff *skb, struct tc_action *a,
0238                int bind, int ref)
0239 {
0240     struct tcf_skbmod *d = to_skbmod(a);
0241     unsigned char *b = skb_tail_pointer(skb);
0242     struct tcf_skbmod_params  *p;
0243     struct tc_skbmod opt = {
0244         .index   = d->tcf_index,
0245         .refcnt  = refcount_read(&d->tcf_refcnt) - ref,
0246         .bindcnt = atomic_read(&d->tcf_bindcnt) - bind,
0247     };
0248     struct tcf_t t;
0249 
0250     spin_lock_bh(&d->tcf_lock);
0251     opt.action = d->tcf_action;
0252     p = rcu_dereference_protected(d->skbmod_p,
0253                       lockdep_is_held(&d->tcf_lock));
0254     opt.flags  = p->flags;
0255     if (nla_put(skb, TCA_SKBMOD_PARMS, sizeof(opt), &opt))
0256         goto nla_put_failure;
0257     if ((p->flags & SKBMOD_F_DMAC) &&
0258         nla_put(skb, TCA_SKBMOD_DMAC, ETH_ALEN, p->eth_dst))
0259         goto nla_put_failure;
0260     if ((p->flags & SKBMOD_F_SMAC) &&
0261         nla_put(skb, TCA_SKBMOD_SMAC, ETH_ALEN, p->eth_src))
0262         goto nla_put_failure;
0263     if ((p->flags & SKBMOD_F_ETYPE) &&
0264         nla_put_u16(skb, TCA_SKBMOD_ETYPE, ntohs(p->eth_type)))
0265         goto nla_put_failure;
0266 
0267     tcf_tm_dump(&t, &d->tcf_tm);
0268     if (nla_put_64bit(skb, TCA_SKBMOD_TM, sizeof(t), &t, TCA_SKBMOD_PAD))
0269         goto nla_put_failure;
0270 
0271     spin_unlock_bh(&d->tcf_lock);
0272     return skb->len;
0273 nla_put_failure:
0274     spin_unlock_bh(&d->tcf_lock);
0275     nlmsg_trim(skb, b);
0276     return -1;
0277 }
0278 
0279 static int tcf_skbmod_walker(struct net *net, struct sk_buff *skb,
0280                  struct netlink_callback *cb, int type,
0281                  const struct tc_action_ops *ops,
0282                  struct netlink_ext_ack *extack)
0283 {
0284     struct tc_action_net *tn = net_generic(net, skbmod_net_id);
0285 
0286     return tcf_generic_walker(tn, skb, cb, type, ops, extack);
0287 }
0288 
0289 static int tcf_skbmod_search(struct net *net, struct tc_action **a, u32 index)
0290 {
0291     struct tc_action_net *tn = net_generic(net, skbmod_net_id);
0292 
0293     return tcf_idr_search(tn, a, index);
0294 }
0295 
0296 static struct tc_action_ops act_skbmod_ops = {
0297     .kind       =   "skbmod",
0298     .id     =   TCA_ACT_SKBMOD,
0299     .owner      =   THIS_MODULE,
0300     .act        =   tcf_skbmod_act,
0301     .dump       =   tcf_skbmod_dump,
0302     .init       =   tcf_skbmod_init,
0303     .cleanup    =   tcf_skbmod_cleanup,
0304     .walk       =   tcf_skbmod_walker,
0305     .lookup     =   tcf_skbmod_search,
0306     .size       =   sizeof(struct tcf_skbmod),
0307 };
0308 
0309 static __net_init int skbmod_init_net(struct net *net)
0310 {
0311     struct tc_action_net *tn = net_generic(net, skbmod_net_id);
0312 
0313     return tc_action_net_init(net, tn, &act_skbmod_ops);
0314 }
0315 
0316 static void __net_exit skbmod_exit_net(struct list_head *net_list)
0317 {
0318     tc_action_net_exit(net_list, skbmod_net_id);
0319 }
0320 
0321 static struct pernet_operations skbmod_net_ops = {
0322     .init = skbmod_init_net,
0323     .exit_batch = skbmod_exit_net,
0324     .id   = &skbmod_net_id,
0325     .size = sizeof(struct tc_action_net),
0326 };
0327 
0328 MODULE_AUTHOR("Jamal Hadi Salim, <jhs@mojatatu.com>");
0329 MODULE_DESCRIPTION("SKB data mod-ing");
0330 MODULE_LICENSE("GPL");
0331 
0332 static int __init skbmod_init_module(void)
0333 {
0334     return tcf_register_action(&act_skbmod_ops, &skbmod_net_ops);
0335 }
0336 
0337 static void __exit skbmod_cleanup_module(void)
0338 {
0339     tcf_unregister_action(&act_skbmod_ops, &skbmod_net_ops);
0340 }
0341 
0342 module_init(skbmod_init_module);
0343 module_exit(skbmod_cleanup_module);