linux/net/sched/act_skbmod.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * net/sched/act_skbmod.c  skb data modifier
   4 *
   5 * Copyright (c) 2016 Jamal Hadi Salim <jhs@mojatatu.com>
   6*/
   7
   8#include <linux/module.h>
   9#include <linux/if_arp.h>
  10#include <linux/init.h>
  11#include <linux/kernel.h>
  12#include <linux/skbuff.h>
  13#include <linux/rtnetlink.h>
  14#include <net/inet_ecn.h>
  15#include <net/netlink.h>
  16#include <net/pkt_sched.h>
  17#include <net/pkt_cls.h>
  18
  19#include <linux/tc_act/tc_skbmod.h>
  20#include <net/tc_act/tc_skbmod.h>
  21
  22static unsigned int skbmod_net_id;
  23static struct tc_action_ops act_skbmod_ops;
  24
  25static int tcf_skbmod_act(struct sk_buff *skb, const struct tc_action *a,
  26                          struct tcf_result *res)
  27{
  28        struct tcf_skbmod *d = to_skbmod(a);
  29        int action, max_edit_len, err;
  30        struct tcf_skbmod_params *p;
  31        u64 flags;
  32
  33        tcf_lastuse_update(&d->tcf_tm);
  34        bstats_cpu_update(this_cpu_ptr(d->common.cpu_bstats), skb);
  35
  36        action = READ_ONCE(d->tcf_action);
  37        if (unlikely(action == TC_ACT_SHOT))
  38                goto drop;
  39
  40        max_edit_len = skb_mac_header_len(skb);
  41        p = rcu_dereference_bh(d->skbmod_p);
  42        flags = p->flags;
  43
  44        /* tcf_skbmod_init() guarantees "flags" to be one of the following:
  45         *      1. a combination of SKBMOD_F_{DMAC,SMAC,ETYPE}
  46         *      2. SKBMOD_F_SWAPMAC
  47         *      3. SKBMOD_F_ECN
  48         * SKBMOD_F_ECN only works with IP packets; all other flags only work with Ethernet
  49         * packets.
  50         */
  51        if (flags == SKBMOD_F_ECN) {
  52                switch (skb_protocol(skb, true)) {
  53                case cpu_to_be16(ETH_P_IP):
  54                case cpu_to_be16(ETH_P_IPV6):
  55                        max_edit_len += skb_network_header_len(skb);
  56                        break;
  57                default:
  58                        goto out;
  59                }
  60        } else if (!skb->dev || skb->dev->type != ARPHRD_ETHER) {
  61                goto out;
  62        }
  63
  64        err = skb_ensure_writable(skb, max_edit_len);
  65        if (unlikely(err)) /* best policy is to drop on the floor */
  66                goto drop;
  67
  68        if (flags & SKBMOD_F_DMAC)
  69                ether_addr_copy(eth_hdr(skb)->h_dest, p->eth_dst);
  70        if (flags & SKBMOD_F_SMAC)
  71                ether_addr_copy(eth_hdr(skb)->h_source, p->eth_src);
  72        if (flags & SKBMOD_F_ETYPE)
  73                eth_hdr(skb)->h_proto = p->eth_type;
  74
  75        if (flags & SKBMOD_F_SWAPMAC) {
  76                u16 tmpaddr[ETH_ALEN / 2]; /* ether_addr_copy() requirement */
  77                /*XXX: I am sure we can come up with more efficient swapping*/
  78                ether_addr_copy((u8 *)tmpaddr, eth_hdr(skb)->h_dest);
  79                ether_addr_copy(eth_hdr(skb)->h_dest, eth_hdr(skb)->h_source);
  80                ether_addr_copy(eth_hdr(skb)->h_source, (u8 *)tmpaddr);
  81        }
  82
  83        if (flags & SKBMOD_F_ECN)
  84                INET_ECN_set_ce(skb);
  85
  86out:
  87        return action;
  88
  89drop:
  90        qstats_overlimit_inc(this_cpu_ptr(d->common.cpu_qstats));
  91        return TC_ACT_SHOT;
  92}
  93
  94static const struct nla_policy skbmod_policy[TCA_SKBMOD_MAX + 1] = {
  95        [TCA_SKBMOD_PARMS]              = { .len = sizeof(struct tc_skbmod) },
  96        [TCA_SKBMOD_DMAC]               = { .len = ETH_ALEN },
  97        [TCA_SKBMOD_SMAC]               = { .len = ETH_ALEN },
  98        [TCA_SKBMOD_ETYPE]              = { .type = NLA_U16 },
  99};
 100
 101static int tcf_skbmod_init(struct net *net, struct nlattr *nla,
 102                           struct nlattr *est, struct tc_action **a,
 103                           struct tcf_proto *tp, u32 flags,
 104                           struct netlink_ext_ack *extack)
 105{
 106        struct tc_action_net *tn = net_generic(net, skbmod_net_id);
 107        bool ovr = flags & TCA_ACT_FLAGS_REPLACE;
 108        bool bind = flags & TCA_ACT_FLAGS_BIND;
 109        struct nlattr *tb[TCA_SKBMOD_MAX + 1];
 110        struct tcf_skbmod_params *p, *p_old;
 111        struct tcf_chain *goto_ch = NULL;
 112        struct tc_skbmod *parm;
 113        u32 lflags = 0, index;
 114        struct tcf_skbmod *d;
 115        bool exists = false;
 116        u8 *daddr = NULL;
 117        u8 *saddr = NULL;
 118        u16 eth_type = 0;
 119        int ret = 0, err;
 120
 121        if (!nla)
 122                return -EINVAL;
 123
 124        err = nla_parse_nested_deprecated(tb, TCA_SKBMOD_MAX, nla,
 125                                          skbmod_policy, NULL);
 126        if (err < 0)
 127                return err;
 128
 129        if (!tb[TCA_SKBMOD_PARMS])
 130                return -EINVAL;
 131
 132        if (tb[TCA_SKBMOD_DMAC]) {
 133                daddr = nla_data(tb[TCA_SKBMOD_DMAC]);
 134                lflags |= SKBMOD_F_DMAC;
 135        }
 136
 137        if (tb[TCA_SKBMOD_SMAC]) {
 138                saddr = nla_data(tb[TCA_SKBMOD_SMAC]);
 139                lflags |= SKBMOD_F_SMAC;
 140        }
 141
 142        if (tb[TCA_SKBMOD_ETYPE]) {
 143                eth_type = nla_get_u16(tb[TCA_SKBMOD_ETYPE]);
 144                lflags |= SKBMOD_F_ETYPE;
 145        }
 146
 147        parm = nla_data(tb[TCA_SKBMOD_PARMS]);
 148        index = parm->index;
 149        if (parm->flags & SKBMOD_F_SWAPMAC)
 150                lflags = SKBMOD_F_SWAPMAC;
 151        if (parm->flags & SKBMOD_F_ECN)
 152                lflags = SKBMOD_F_ECN;
 153
 154        err = tcf_idr_check_alloc(tn, &index, a, bind);
 155        if (err < 0)
 156                return err;
 157        exists = err;
 158        if (exists && bind)
 159                return 0;
 160
 161        if (!lflags) {
 162                if (exists)
 163                        tcf_idr_release(*a, bind);
 164                else
 165                        tcf_idr_cleanup(tn, index);
 166                return -EINVAL;
 167        }
 168
 169        if (!exists) {
 170                ret = tcf_idr_create(tn, index, est, a,
 171                                     &act_skbmod_ops, bind, true, 0);
 172                if (ret) {
 173                        tcf_idr_cleanup(tn, index);
 174                        return ret;
 175                }
 176
 177                ret = ACT_P_CREATED;
 178        } else if (!ovr) {
 179                tcf_idr_release(*a, bind);
 180                return -EEXIST;
 181        }
 182        err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
 183        if (err < 0)
 184                goto release_idr;
 185
 186        d = to_skbmod(*a);
 187
 188        p = kzalloc(sizeof(struct tcf_skbmod_params), GFP_KERNEL);
 189        if (unlikely(!p)) {
 190                err = -ENOMEM;
 191                goto put_chain;
 192        }
 193
 194        p->flags = lflags;
 195
 196        if (ovr)
 197                spin_lock_bh(&d->tcf_lock);
 198        /* Protected by tcf_lock if overwriting existing action. */
 199        goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
 200        p_old = rcu_dereference_protected(d->skbmod_p, 1);
 201
 202        if (lflags & SKBMOD_F_DMAC)
 203                ether_addr_copy(p->eth_dst, daddr);
 204        if (lflags & SKBMOD_F_SMAC)
 205                ether_addr_copy(p->eth_src, saddr);
 206        if (lflags & SKBMOD_F_ETYPE)
 207                p->eth_type = htons(eth_type);
 208
 209        rcu_assign_pointer(d->skbmod_p, p);
 210        if (ovr)
 211                spin_unlock_bh(&d->tcf_lock);
 212
 213        if (p_old)
 214                kfree_rcu(p_old, rcu);
 215        if (goto_ch)
 216                tcf_chain_put_by_act(goto_ch);
 217
 218        return ret;
 219put_chain:
 220        if (goto_ch)
 221                tcf_chain_put_by_act(goto_ch);
 222release_idr:
 223        tcf_idr_release(*a, bind);
 224        return err;
 225}
 226
 227static void tcf_skbmod_cleanup(struct tc_action *a)
 228{
 229        struct tcf_skbmod *d = to_skbmod(a);
 230        struct tcf_skbmod_params  *p;
 231
 232        p = rcu_dereference_protected(d->skbmod_p, 1);
 233        if (p)
 234                kfree_rcu(p, rcu);
 235}
 236
 237static int tcf_skbmod_dump(struct sk_buff *skb, struct tc_action *a,
 238                           int bind, int ref)
 239{
 240        struct tcf_skbmod *d = to_skbmod(a);
 241        unsigned char *b = skb_tail_pointer(skb);
 242        struct tcf_skbmod_params  *p;
 243        struct tc_skbmod opt = {
 244                .index   = d->tcf_index,
 245                .refcnt  = refcount_read(&d->tcf_refcnt) - ref,
 246                .bindcnt = atomic_read(&d->tcf_bindcnt) - bind,
 247        };
 248        struct tcf_t t;
 249
 250        spin_lock_bh(&d->tcf_lock);
 251        opt.action = d->tcf_action;
 252        p = rcu_dereference_protected(d->skbmod_p,
 253                                      lockdep_is_held(&d->tcf_lock));
 254        opt.flags  = p->flags;
 255        if (nla_put(skb, TCA_SKBMOD_PARMS, sizeof(opt), &opt))
 256                goto nla_put_failure;
 257        if ((p->flags & SKBMOD_F_DMAC) &&
 258            nla_put(skb, TCA_SKBMOD_DMAC, ETH_ALEN, p->eth_dst))
 259                goto nla_put_failure;
 260        if ((p->flags & SKBMOD_F_SMAC) &&
 261            nla_put(skb, TCA_SKBMOD_SMAC, ETH_ALEN, p->eth_src))
 262                goto nla_put_failure;
 263        if ((p->flags & SKBMOD_F_ETYPE) &&
 264            nla_put_u16(skb, TCA_SKBMOD_ETYPE, ntohs(p->eth_type)))
 265                goto nla_put_failure;
 266
 267        tcf_tm_dump(&t, &d->tcf_tm);
 268        if (nla_put_64bit(skb, TCA_SKBMOD_TM, sizeof(t), &t, TCA_SKBMOD_PAD))
 269                goto nla_put_failure;
 270
 271        spin_unlock_bh(&d->tcf_lock);
 272        return skb->len;
 273nla_put_failure:
 274        spin_unlock_bh(&d->tcf_lock);
 275        nlmsg_trim(skb, b);
 276        return -1;
 277}
 278
 279static int tcf_skbmod_walker(struct net *net, struct sk_buff *skb,
 280                             struct netlink_callback *cb, int type,
 281                             const struct tc_action_ops *ops,
 282                             struct netlink_ext_ack *extack)
 283{
 284        struct tc_action_net *tn = net_generic(net, skbmod_net_id);
 285
 286        return tcf_generic_walker(tn, skb, cb, type, ops, extack);
 287}
 288
 289static int tcf_skbmod_search(struct net *net, struct tc_action **a, u32 index)
 290{
 291        struct tc_action_net *tn = net_generic(net, skbmod_net_id);
 292
 293        return tcf_idr_search(tn, a, index);
 294}
 295
 296static struct tc_action_ops act_skbmod_ops = {
 297        .kind           =       "skbmod",
 298        .id             =       TCA_ACT_SKBMOD,
 299        .owner          =       THIS_MODULE,
 300        .act            =       tcf_skbmod_act,
 301        .dump           =       tcf_skbmod_dump,
 302        .init           =       tcf_skbmod_init,
 303        .cleanup        =       tcf_skbmod_cleanup,
 304        .walk           =       tcf_skbmod_walker,
 305        .lookup         =       tcf_skbmod_search,
 306        .size           =       sizeof(struct tcf_skbmod),
 307};
 308
 309static __net_init int skbmod_init_net(struct net *net)
 310{
 311        struct tc_action_net *tn = net_generic(net, skbmod_net_id);
 312
 313        return tc_action_net_init(net, tn, &act_skbmod_ops);
 314}
 315
 316static void __net_exit skbmod_exit_net(struct list_head *net_list)
 317{
 318        tc_action_net_exit(net_list, skbmod_net_id);
 319}
 320
 321static struct pernet_operations skbmod_net_ops = {
 322        .init = skbmod_init_net,
 323        .exit_batch = skbmod_exit_net,
 324        .id   = &skbmod_net_id,
 325        .size = sizeof(struct tc_action_net),
 326};
 327
 328MODULE_AUTHOR("Jamal Hadi Salim, <jhs@mojatatu.com>");
 329MODULE_DESCRIPTION("SKB data mod-ing");
 330MODULE_LICENSE("GPL");
 331
 332static int __init skbmod_init_module(void)
 333{
 334        return tcf_register_action(&act_skbmod_ops, &skbmod_net_ops);
 335}
 336
 337static void __exit skbmod_cleanup_module(void)
 338{
 339        tcf_unregister_action(&act_skbmod_ops, &skbmod_net_ops);
 340}
 341
 342module_init(skbmod_init_module);
 343module_exit(skbmod_cleanup_module);
 344