Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * net/sched/act_pedit.c    Generic packet editor
0004  *
0005  * Authors: Jamal Hadi Salim (2002-4)
0006  */
0007 
0008 #include <linux/types.h>
0009 #include <linux/kernel.h>
0010 #include <linux/string.h>
0011 #include <linux/errno.h>
0012 #include <linux/skbuff.h>
0013 #include <linux/rtnetlink.h>
0014 #include <linux/module.h>
0015 #include <linux/init.h>
0016 #include <linux/slab.h>
0017 #include <net/netlink.h>
0018 #include <net/pkt_sched.h>
0019 #include <linux/tc_act/tc_pedit.h>
0020 #include <net/tc_act/tc_pedit.h>
0021 #include <uapi/linux/tc_act/tc_pedit.h>
0022 #include <net/pkt_cls.h>
0023 
0024 static unsigned int pedit_net_id;
0025 static struct tc_action_ops act_pedit_ops;
0026 
0027 static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = {
0028     [TCA_PEDIT_PARMS]   = { .len = sizeof(struct tc_pedit) },
0029     [TCA_PEDIT_KEYS_EX]   = { .type = NLA_NESTED },
0030 };
0031 
0032 static const struct nla_policy pedit_key_ex_policy[TCA_PEDIT_KEY_EX_MAX + 1] = {
0033     [TCA_PEDIT_KEY_EX_HTYPE]  = { .type = NLA_U16 },
0034     [TCA_PEDIT_KEY_EX_CMD]    = { .type = NLA_U16 },
0035 };
0036 
0037 static struct tcf_pedit_key_ex *tcf_pedit_keys_ex_parse(struct nlattr *nla,
0038                             u8 n)
0039 {
0040     struct tcf_pedit_key_ex *keys_ex;
0041     struct tcf_pedit_key_ex *k;
0042     const struct nlattr *ka;
0043     int err = -EINVAL;
0044     int rem;
0045 
0046     if (!nla)
0047         return NULL;
0048 
0049     keys_ex = kcalloc(n, sizeof(*k), GFP_KERNEL);
0050     if (!keys_ex)
0051         return ERR_PTR(-ENOMEM);
0052 
0053     k = keys_ex;
0054 
0055     nla_for_each_nested(ka, nla, rem) {
0056         struct nlattr *tb[TCA_PEDIT_KEY_EX_MAX + 1];
0057 
0058         if (!n) {
0059             err = -EINVAL;
0060             goto err_out;
0061         }
0062         n--;
0063 
0064         if (nla_type(ka) != TCA_PEDIT_KEY_EX) {
0065             err = -EINVAL;
0066             goto err_out;
0067         }
0068 
0069         err = nla_parse_nested_deprecated(tb, TCA_PEDIT_KEY_EX_MAX,
0070                           ka, pedit_key_ex_policy,
0071                           NULL);
0072         if (err)
0073             goto err_out;
0074 
0075         if (!tb[TCA_PEDIT_KEY_EX_HTYPE] ||
0076             !tb[TCA_PEDIT_KEY_EX_CMD]) {
0077             err = -EINVAL;
0078             goto err_out;
0079         }
0080 
0081         k->htype = nla_get_u16(tb[TCA_PEDIT_KEY_EX_HTYPE]);
0082         k->cmd = nla_get_u16(tb[TCA_PEDIT_KEY_EX_CMD]);
0083 
0084         if (k->htype > TCA_PEDIT_HDR_TYPE_MAX ||
0085             k->cmd > TCA_PEDIT_CMD_MAX) {
0086             err = -EINVAL;
0087             goto err_out;
0088         }
0089 
0090         k++;
0091     }
0092 
0093     if (n) {
0094         err = -EINVAL;
0095         goto err_out;
0096     }
0097 
0098     return keys_ex;
0099 
0100 err_out:
0101     kfree(keys_ex);
0102     return ERR_PTR(err);
0103 }
0104 
0105 static int tcf_pedit_key_ex_dump(struct sk_buff *skb,
0106                  struct tcf_pedit_key_ex *keys_ex, int n)
0107 {
0108     struct nlattr *keys_start = nla_nest_start_noflag(skb,
0109                               TCA_PEDIT_KEYS_EX);
0110 
0111     if (!keys_start)
0112         goto nla_failure;
0113     for (; n > 0; n--) {
0114         struct nlattr *key_start;
0115 
0116         key_start = nla_nest_start_noflag(skb, TCA_PEDIT_KEY_EX);
0117         if (!key_start)
0118             goto nla_failure;
0119 
0120         if (nla_put_u16(skb, TCA_PEDIT_KEY_EX_HTYPE, keys_ex->htype) ||
0121             nla_put_u16(skb, TCA_PEDIT_KEY_EX_CMD, keys_ex->cmd))
0122             goto nla_failure;
0123 
0124         nla_nest_end(skb, key_start);
0125 
0126         keys_ex++;
0127     }
0128 
0129     nla_nest_end(skb, keys_start);
0130 
0131     return 0;
0132 nla_failure:
0133     nla_nest_cancel(skb, keys_start);
0134     return -EINVAL;
0135 }
0136 
0137 static int tcf_pedit_init(struct net *net, struct nlattr *nla,
0138               struct nlattr *est, struct tc_action **a,
0139               struct tcf_proto *tp, u32 flags,
0140               struct netlink_ext_ack *extack)
0141 {
0142     struct tc_action_net *tn = net_generic(net, pedit_net_id);
0143     bool bind = flags & TCA_ACT_FLAGS_BIND;
0144     struct nlattr *tb[TCA_PEDIT_MAX + 1];
0145     struct tcf_chain *goto_ch = NULL;
0146     struct tc_pedit_key *keys = NULL;
0147     struct tcf_pedit_key_ex *keys_ex;
0148     struct tc_pedit *parm;
0149     struct nlattr *pattr;
0150     struct tcf_pedit *p;
0151     int ret = 0, err;
0152     int i, ksize;
0153     u32 index;
0154 
0155     if (!nla) {
0156         NL_SET_ERR_MSG_MOD(extack, "Pedit requires attributes to be passed");
0157         return -EINVAL;
0158     }
0159 
0160     err = nla_parse_nested_deprecated(tb, TCA_PEDIT_MAX, nla,
0161                       pedit_policy, NULL);
0162     if (err < 0)
0163         return err;
0164 
0165     pattr = tb[TCA_PEDIT_PARMS];
0166     if (!pattr)
0167         pattr = tb[TCA_PEDIT_PARMS_EX];
0168     if (!pattr) {
0169         NL_SET_ERR_MSG_MOD(extack, "Missing required TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute");
0170         return -EINVAL;
0171     }
0172 
0173     parm = nla_data(pattr);
0174     if (!parm->nkeys) {
0175         NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed");
0176         return -EINVAL;
0177     }
0178     ksize = parm->nkeys * sizeof(struct tc_pedit_key);
0179     if (nla_len(pattr) < sizeof(*parm) + ksize) {
0180         NL_SET_ERR_MSG_ATTR(extack, pattr, "Length of TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute is invalid");
0181         return -EINVAL;
0182     }
0183 
0184     keys_ex = tcf_pedit_keys_ex_parse(tb[TCA_PEDIT_KEYS_EX], parm->nkeys);
0185     if (IS_ERR(keys_ex))
0186         return PTR_ERR(keys_ex);
0187 
0188     index = parm->index;
0189     err = tcf_idr_check_alloc(tn, &index, a, bind);
0190     if (!err) {
0191         ret = tcf_idr_create(tn, index, est, a,
0192                      &act_pedit_ops, bind, false, flags);
0193         if (ret) {
0194             tcf_idr_cleanup(tn, index);
0195             goto out_free;
0196         }
0197         ret = ACT_P_CREATED;
0198     } else if (err > 0) {
0199         if (bind)
0200             goto out_free;
0201         if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
0202             ret = -EEXIST;
0203             goto out_release;
0204         }
0205     } else {
0206         ret = err;
0207         goto out_free;
0208     }
0209 
0210     err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
0211     if (err < 0) {
0212         ret = err;
0213         goto out_release;
0214     }
0215     p = to_pedit(*a);
0216     spin_lock_bh(&p->tcf_lock);
0217 
0218     if (ret == ACT_P_CREATED ||
0219         (p->tcfp_nkeys && p->tcfp_nkeys != parm->nkeys)) {
0220         keys = kmalloc(ksize, GFP_ATOMIC);
0221         if (!keys) {
0222             spin_unlock_bh(&p->tcf_lock);
0223             ret = -ENOMEM;
0224             goto put_chain;
0225         }
0226         kfree(p->tcfp_keys);
0227         p->tcfp_keys = keys;
0228         p->tcfp_nkeys = parm->nkeys;
0229     }
0230     memcpy(p->tcfp_keys, parm->keys, ksize);
0231     p->tcfp_off_max_hint = 0;
0232     for (i = 0; i < p->tcfp_nkeys; ++i) {
0233         u32 cur = p->tcfp_keys[i].off;
0234 
0235         /* sanitize the shift value for any later use */
0236         p->tcfp_keys[i].shift = min_t(size_t, BITS_PER_TYPE(int) - 1,
0237                           p->tcfp_keys[i].shift);
0238 
0239         /* The AT option can read a single byte, we can bound the actual
0240          * value with uchar max.
0241          */
0242         cur += (0xff & p->tcfp_keys[i].offmask) >> p->tcfp_keys[i].shift;
0243 
0244         /* Each key touches 4 bytes starting from the computed offset */
0245         p->tcfp_off_max_hint = max(p->tcfp_off_max_hint, cur + 4);
0246     }
0247 
0248     p->tcfp_flags = parm->flags;
0249     goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
0250 
0251     kfree(p->tcfp_keys_ex);
0252     p->tcfp_keys_ex = keys_ex;
0253 
0254     spin_unlock_bh(&p->tcf_lock);
0255     if (goto_ch)
0256         tcf_chain_put_by_act(goto_ch);
0257     return ret;
0258 
0259 put_chain:
0260     if (goto_ch)
0261         tcf_chain_put_by_act(goto_ch);
0262 out_release:
0263     tcf_idr_release(*a, bind);
0264 out_free:
0265     kfree(keys_ex);
0266     return ret;
0267 
0268 }
0269 
0270 static void tcf_pedit_cleanup(struct tc_action *a)
0271 {
0272     struct tcf_pedit *p = to_pedit(a);
0273     struct tc_pedit_key *keys = p->tcfp_keys;
0274 
0275     kfree(keys);
0276     kfree(p->tcfp_keys_ex);
0277 }
0278 
0279 static bool offset_valid(struct sk_buff *skb, int offset)
0280 {
0281     if (offset > 0 && offset > skb->len)
0282         return false;
0283 
0284     if  (offset < 0 && -offset > skb_headroom(skb))
0285         return false;
0286 
0287     return true;
0288 }
0289 
0290 static int pedit_skb_hdr_offset(struct sk_buff *skb,
0291                 enum pedit_header_type htype, int *hoffset)
0292 {
0293     int ret = -EINVAL;
0294 
0295     switch (htype) {
0296     case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
0297         if (skb_mac_header_was_set(skb)) {
0298             *hoffset = skb_mac_offset(skb);
0299             ret = 0;
0300         }
0301         break;
0302     case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK:
0303     case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
0304     case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
0305         *hoffset = skb_network_offset(skb);
0306         ret = 0;
0307         break;
0308     case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
0309     case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
0310         if (skb_transport_header_was_set(skb)) {
0311             *hoffset = skb_transport_offset(skb);
0312             ret = 0;
0313         }
0314         break;
0315     default:
0316         ret = -EINVAL;
0317         break;
0318     }
0319 
0320     return ret;
0321 }
0322 
0323 static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
0324              struct tcf_result *res)
0325 {
0326     struct tcf_pedit *p = to_pedit(a);
0327     u32 max_offset;
0328     int i;
0329 
0330     spin_lock(&p->tcf_lock);
0331 
0332     max_offset = (skb_transport_header_was_set(skb) ?
0333               skb_transport_offset(skb) :
0334               skb_network_offset(skb)) +
0335              p->tcfp_off_max_hint;
0336     if (skb_ensure_writable(skb, min(skb->len, max_offset)))
0337         goto unlock;
0338 
0339     tcf_lastuse_update(&p->tcf_tm);
0340 
0341     if (p->tcfp_nkeys > 0) {
0342         struct tc_pedit_key *tkey = p->tcfp_keys;
0343         struct tcf_pedit_key_ex *tkey_ex = p->tcfp_keys_ex;
0344         enum pedit_header_type htype =
0345             TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
0346         enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET;
0347 
0348         for (i = p->tcfp_nkeys; i > 0; i--, tkey++) {
0349             u32 *ptr, hdata;
0350             int offset = tkey->off;
0351             int hoffset;
0352             u32 val;
0353             int rc;
0354 
0355             if (tkey_ex) {
0356                 htype = tkey_ex->htype;
0357                 cmd = tkey_ex->cmd;
0358 
0359                 tkey_ex++;
0360             }
0361 
0362             rc = pedit_skb_hdr_offset(skb, htype, &hoffset);
0363             if (rc) {
0364                 pr_info("tc action pedit bad header type specified (0x%x)\n",
0365                     htype);
0366                 goto bad;
0367             }
0368 
0369             if (tkey->offmask) {
0370                 u8 *d, _d;
0371 
0372                 if (!offset_valid(skb, hoffset + tkey->at)) {
0373                     pr_info("tc action pedit 'at' offset %d out of bounds\n",
0374                         hoffset + tkey->at);
0375                     goto bad;
0376                 }
0377                 d = skb_header_pointer(skb, hoffset + tkey->at,
0378                                sizeof(_d), &_d);
0379                 if (!d)
0380                     goto bad;
0381                 offset += (*d & tkey->offmask) >> tkey->shift;
0382             }
0383 
0384             if (offset % 4) {
0385                 pr_info("tc action pedit offset must be on 32 bit boundaries\n");
0386                 goto bad;
0387             }
0388 
0389             if (!offset_valid(skb, hoffset + offset)) {
0390                 pr_info("tc action pedit offset %d out of bounds\n",
0391                     hoffset + offset);
0392                 goto bad;
0393             }
0394 
0395             ptr = skb_header_pointer(skb, hoffset + offset,
0396                          sizeof(hdata), &hdata);
0397             if (!ptr)
0398                 goto bad;
0399             /* just do it, baby */
0400             switch (cmd) {
0401             case TCA_PEDIT_KEY_EX_CMD_SET:
0402                 val = tkey->val;
0403                 break;
0404             case TCA_PEDIT_KEY_EX_CMD_ADD:
0405                 val = (*ptr + tkey->val) & ~tkey->mask;
0406                 break;
0407             default:
0408                 pr_info("tc action pedit bad command (%d)\n",
0409                     cmd);
0410                 goto bad;
0411             }
0412 
0413             *ptr = ((*ptr & tkey->mask) ^ val);
0414             if (ptr == &hdata)
0415                 skb_store_bits(skb, hoffset + offset, ptr, 4);
0416         }
0417 
0418         goto done;
0419     } else {
0420         WARN(1, "pedit BUG: index %d\n", p->tcf_index);
0421     }
0422 
0423 bad:
0424     p->tcf_qstats.overlimits++;
0425 done:
0426     bstats_update(&p->tcf_bstats, skb);
0427 unlock:
0428     spin_unlock(&p->tcf_lock);
0429     return p->tcf_action;
0430 }
0431 
0432 static void tcf_pedit_stats_update(struct tc_action *a, u64 bytes, u64 packets,
0433                    u64 drops, u64 lastuse, bool hw)
0434 {
0435     struct tcf_pedit *d = to_pedit(a);
0436     struct tcf_t *tm = &d->tcf_tm;
0437 
0438     tcf_action_update_stats(a, bytes, packets, drops, hw);
0439     tm->lastuse = max_t(u64, tm->lastuse, lastuse);
0440 }
0441 
0442 static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a,
0443               int bind, int ref)
0444 {
0445     unsigned char *b = skb_tail_pointer(skb);
0446     struct tcf_pedit *p = to_pedit(a);
0447     struct tc_pedit *opt;
0448     struct tcf_t t;
0449     int s;
0450 
0451     s = struct_size(opt, keys, p->tcfp_nkeys);
0452 
0453     /* netlink spinlocks held above us - must use ATOMIC */
0454     opt = kzalloc(s, GFP_ATOMIC);
0455     if (unlikely(!opt))
0456         return -ENOBUFS;
0457 
0458     spin_lock_bh(&p->tcf_lock);
0459     memcpy(opt->keys, p->tcfp_keys, flex_array_size(opt, keys, p->tcfp_nkeys));
0460     opt->index = p->tcf_index;
0461     opt->nkeys = p->tcfp_nkeys;
0462     opt->flags = p->tcfp_flags;
0463     opt->action = p->tcf_action;
0464     opt->refcnt = refcount_read(&p->tcf_refcnt) - ref;
0465     opt->bindcnt = atomic_read(&p->tcf_bindcnt) - bind;
0466 
0467     if (p->tcfp_keys_ex) {
0468         if (tcf_pedit_key_ex_dump(skb,
0469                       p->tcfp_keys_ex,
0470                       p->tcfp_nkeys))
0471             goto nla_put_failure;
0472 
0473         if (nla_put(skb, TCA_PEDIT_PARMS_EX, s, opt))
0474             goto nla_put_failure;
0475     } else {
0476         if (nla_put(skb, TCA_PEDIT_PARMS, s, opt))
0477             goto nla_put_failure;
0478     }
0479 
0480     tcf_tm_dump(&t, &p->tcf_tm);
0481     if (nla_put_64bit(skb, TCA_PEDIT_TM, sizeof(t), &t, TCA_PEDIT_PAD))
0482         goto nla_put_failure;
0483     spin_unlock_bh(&p->tcf_lock);
0484 
0485     kfree(opt);
0486     return skb->len;
0487 
0488 nla_put_failure:
0489     spin_unlock_bh(&p->tcf_lock);
0490     nlmsg_trim(skb, b);
0491     kfree(opt);
0492     return -1;
0493 }
0494 
0495 static int tcf_pedit_walker(struct net *net, struct sk_buff *skb,
0496                 struct netlink_callback *cb, int type,
0497                 const struct tc_action_ops *ops,
0498                 struct netlink_ext_ack *extack)
0499 {
0500     struct tc_action_net *tn = net_generic(net, pedit_net_id);
0501 
0502     return tcf_generic_walker(tn, skb, cb, type, ops, extack);
0503 }
0504 
0505 static int tcf_pedit_search(struct net *net, struct tc_action **a, u32 index)
0506 {
0507     struct tc_action_net *tn = net_generic(net, pedit_net_id);
0508 
0509     return tcf_idr_search(tn, a, index);
0510 }
0511 
0512 static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data,
0513                        u32 *index_inc, bool bind,
0514                        struct netlink_ext_ack *extack)
0515 {
0516     if (bind) {
0517         struct flow_action_entry *entry = entry_data;
0518         int k;
0519 
0520         for (k = 0; k < tcf_pedit_nkeys(act); k++) {
0521             switch (tcf_pedit_cmd(act, k)) {
0522             case TCA_PEDIT_KEY_EX_CMD_SET:
0523                 entry->id = FLOW_ACTION_MANGLE;
0524                 break;
0525             case TCA_PEDIT_KEY_EX_CMD_ADD:
0526                 entry->id = FLOW_ACTION_ADD;
0527                 break;
0528             default:
0529                 NL_SET_ERR_MSG_MOD(extack, "Unsupported pedit command offload");
0530                 return -EOPNOTSUPP;
0531             }
0532             entry->mangle.htype = tcf_pedit_htype(act, k);
0533             entry->mangle.mask = tcf_pedit_mask(act, k);
0534             entry->mangle.val = tcf_pedit_val(act, k);
0535             entry->mangle.offset = tcf_pedit_offset(act, k);
0536             entry->hw_stats = tc_act_hw_stats(act->hw_stats);
0537             entry++;
0538         }
0539         *index_inc = k;
0540     } else {
0541         return -EOPNOTSUPP;
0542     }
0543 
0544     return 0;
0545 }
0546 
0547 static struct tc_action_ops act_pedit_ops = {
0548     .kind       =   "pedit",
0549     .id     =   TCA_ID_PEDIT,
0550     .owner      =   THIS_MODULE,
0551     .act        =   tcf_pedit_act,
0552     .stats_update   =   tcf_pedit_stats_update,
0553     .dump       =   tcf_pedit_dump,
0554     .cleanup    =   tcf_pedit_cleanup,
0555     .init       =   tcf_pedit_init,
0556     .walk       =   tcf_pedit_walker,
0557     .lookup     =   tcf_pedit_search,
0558     .offload_act_setup =    tcf_pedit_offload_act_setup,
0559     .size       =   sizeof(struct tcf_pedit),
0560 };
0561 
0562 static __net_init int pedit_init_net(struct net *net)
0563 {
0564     struct tc_action_net *tn = net_generic(net, pedit_net_id);
0565 
0566     return tc_action_net_init(net, tn, &act_pedit_ops);
0567 }
0568 
0569 static void __net_exit pedit_exit_net(struct list_head *net_list)
0570 {
0571     tc_action_net_exit(net_list, pedit_net_id);
0572 }
0573 
0574 static struct pernet_operations pedit_net_ops = {
0575     .init = pedit_init_net,
0576     .exit_batch = pedit_exit_net,
0577     .id   = &pedit_net_id,
0578     .size = sizeof(struct tc_action_net),
0579 };
0580 
0581 MODULE_AUTHOR("Jamal Hadi Salim(2002-4)");
0582 MODULE_DESCRIPTION("Generic Packet Editor actions");
0583 MODULE_LICENSE("GPL");
0584 
0585 static int __init pedit_init_module(void)
0586 {
0587     return tcf_register_action(&act_pedit_ops, &pedit_net_ops);
0588 }
0589 
0590 static void __exit pedit_cleanup_module(void)
0591 {
0592     tcf_unregister_action(&act_pedit_ops, &pedit_net_ops);
0593 }
0594 
0595 module_init(pedit_init_module);
0596 module_exit(pedit_cleanup_module);