GNU Linux-libre 4.14.332-gnu1
[releases.git] / net / sched / act_pedit.c
1 /*
2  * net/sched/act_pedit.c        Generic packet editor
3  *
4  *              This program is free software; you can redistribute it and/or
5  *              modify it under the terms of the GNU General Public License
6  *              as published by the Free Software Foundation; either version
7  *              2 of the License, or (at your option) any later version.
8  *
9  * Authors:     Jamal Hadi Salim (2002-4)
10  */
11
12 #include <linux/types.h>
13 #include <linux/kernel.h>
14 #include <linux/string.h>
15 #include <linux/errno.h>
16 #include <linux/skbuff.h>
17 #include <linux/rtnetlink.h>
18 #include <linux/module.h>
19 #include <linux/init.h>
20 #include <linux/slab.h>
21 #include <net/netlink.h>
22 #include <net/pkt_sched.h>
23 #include <linux/tc_act/tc_pedit.h>
24 #include <net/tc_act/tc_pedit.h>
25 #include <uapi/linux/tc_act/tc_pedit.h>
26
27 static unsigned int pedit_net_id;
28 static struct tc_action_ops act_pedit_ops;
29
30 static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = {
31         [TCA_PEDIT_PARMS]       = { .len = sizeof(struct tc_pedit) },
32         [TCA_PEDIT_PARMS_EX]    = { .len = sizeof(struct tc_pedit) },
33         [TCA_PEDIT_KEYS_EX]   = { .type = NLA_NESTED },
34 };
35
36 static const struct nla_policy pedit_key_ex_policy[TCA_PEDIT_KEY_EX_MAX + 1] = {
37         [TCA_PEDIT_KEY_EX_HTYPE]  = { .type = NLA_U16 },
38         [TCA_PEDIT_KEY_EX_CMD]    = { .type = NLA_U16 },
39 };
40
41 static struct tcf_pedit_key_ex *tcf_pedit_keys_ex_parse(struct nlattr *nla,
42                                                         u8 n)
43 {
44         struct tcf_pedit_key_ex *keys_ex;
45         struct tcf_pedit_key_ex *k;
46         const struct nlattr *ka;
47         int err = -EINVAL;
48         int rem;
49
50         if (!nla)
51                 return NULL;
52
53         keys_ex = kcalloc(n, sizeof(*k), GFP_KERNEL);
54         if (!keys_ex)
55                 return ERR_PTR(-ENOMEM);
56
57         k = keys_ex;
58
59         nla_for_each_nested(ka, nla, rem) {
60                 struct nlattr *tb[TCA_PEDIT_KEY_EX_MAX + 1];
61
62                 if (!n) {
63                         err = -EINVAL;
64                         goto err_out;
65                 }
66                 n--;
67
68                 if (nla_type(ka) != TCA_PEDIT_KEY_EX) {
69                         err = -EINVAL;
70                         goto err_out;
71                 }
72
73                 err = nla_parse_nested(tb, TCA_PEDIT_KEY_EX_MAX, ka,
74                                        pedit_key_ex_policy, NULL);
75                 if (err)
76                         goto err_out;
77
78                 if (!tb[TCA_PEDIT_KEY_EX_HTYPE] ||
79                     !tb[TCA_PEDIT_KEY_EX_CMD]) {
80                         err = -EINVAL;
81                         goto err_out;
82                 }
83
84                 k->htype = nla_get_u16(tb[TCA_PEDIT_KEY_EX_HTYPE]);
85                 k->cmd = nla_get_u16(tb[TCA_PEDIT_KEY_EX_CMD]);
86
87                 if (k->htype > TCA_PEDIT_HDR_TYPE_MAX ||
88                     k->cmd > TCA_PEDIT_CMD_MAX) {
89                         err = -EINVAL;
90                         goto err_out;
91                 }
92
93                 k++;
94         }
95
96         if (n) {
97                 err = -EINVAL;
98                 goto err_out;
99         }
100
101         return keys_ex;
102
103 err_out:
104         kfree(keys_ex);
105         return ERR_PTR(err);
106 }
107
108 static int tcf_pedit_key_ex_dump(struct sk_buff *skb,
109                                  struct tcf_pedit_key_ex *keys_ex, int n)
110 {
111         struct nlattr *keys_start = nla_nest_start(skb, TCA_PEDIT_KEYS_EX);
112
113         if (!keys_start)
114                 goto nla_failure;
115         for (; n > 0; n--) {
116                 struct nlattr *key_start;
117
118                 key_start = nla_nest_start(skb, TCA_PEDIT_KEY_EX);
119                 if (!key_start)
120                         goto nla_failure;
121
122                 if (nla_put_u16(skb, TCA_PEDIT_KEY_EX_HTYPE, keys_ex->htype) ||
123                     nla_put_u16(skb, TCA_PEDIT_KEY_EX_CMD, keys_ex->cmd))
124                         goto nla_failure;
125
126                 nla_nest_end(skb, key_start);
127
128                 keys_ex++;
129         }
130
131         nla_nest_end(skb, keys_start);
132
133         return 0;
134 nla_failure:
135         nla_nest_cancel(skb, keys_start);
136         return -EINVAL;
137 }
138
139 static int tcf_pedit_init(struct net *net, struct nlattr *nla,
140                           struct nlattr *est, struct tc_action **a,
141                           int ovr, int bind)
142 {
143         struct tc_action_net *tn = net_generic(net, pedit_net_id);
144         struct nlattr *tb[TCA_PEDIT_MAX + 1];
145         struct nlattr *pattr;
146         struct tc_pedit *parm;
147         int ret = 0, err;
148         struct tcf_pedit *p;
149         struct tc_pedit_key *keys = NULL;
150         struct tcf_pedit_key_ex *keys_ex;
151         int ksize;
152
153         if (nla == NULL)
154                 return -EINVAL;
155
156         err = nla_parse_nested(tb, TCA_PEDIT_MAX, nla, pedit_policy, NULL);
157         if (err < 0)
158                 return err;
159
160         pattr = tb[TCA_PEDIT_PARMS];
161         if (!pattr)
162                 pattr = tb[TCA_PEDIT_PARMS_EX];
163         if (!pattr)
164                 return -EINVAL;
165
166         parm = nla_data(pattr);
167         if (!parm->nkeys)
168                 return -EINVAL;
169
170         ksize = parm->nkeys * sizeof(struct tc_pedit_key);
171         if (nla_len(pattr) < sizeof(*parm) + ksize)
172                 return -EINVAL;
173
174         keys_ex = tcf_pedit_keys_ex_parse(tb[TCA_PEDIT_KEYS_EX], parm->nkeys);
175         if (IS_ERR(keys_ex))
176                 return PTR_ERR(keys_ex);
177
178         if (!tcf_idr_check(tn, parm->index, a, bind)) {
179                 ret = tcf_idr_create(tn, parm->index, est, a,
180                                      &act_pedit_ops, bind, false);
181                 if (ret)
182                         return ret;
183                 p = to_pedit(*a);
184                 keys = kmalloc(ksize, GFP_KERNEL);
185                 if (keys == NULL) {
186                         tcf_idr_release(*a, bind);
187                         kfree(keys_ex);
188                         return -ENOMEM;
189                 }
190                 ret = ACT_P_CREATED;
191         } else {
192                 if (bind)
193                         return 0;
194                 tcf_idr_release(*a, bind);
195                 if (!ovr)
196                         return -EEXIST;
197                 p = to_pedit(*a);
198                 if (p->tcfp_nkeys && p->tcfp_nkeys != parm->nkeys) {
199                         keys = kmalloc(ksize, GFP_KERNEL);
200                         if (!keys) {
201                                 kfree(keys_ex);
202                                 return -ENOMEM;
203                         }
204                 }
205         }
206
207         spin_lock_bh(&p->tcf_lock);
208         p->tcfp_flags = parm->flags;
209         p->tcf_action = parm->action;
210         if (keys) {
211                 kfree(p->tcfp_keys);
212                 p->tcfp_keys = keys;
213                 p->tcfp_nkeys = parm->nkeys;
214         }
215         memcpy(p->tcfp_keys, parm->keys, ksize);
216
217         kfree(p->tcfp_keys_ex);
218         p->tcfp_keys_ex = keys_ex;
219
220         spin_unlock_bh(&p->tcf_lock);
221         if (ret == ACT_P_CREATED)
222                 tcf_idr_insert(tn, *a);
223         return ret;
224 }
225
226 static void tcf_pedit_cleanup(struct tc_action *a, int bind)
227 {
228         struct tcf_pedit *p = to_pedit(a);
229         struct tc_pedit_key *keys = p->tcfp_keys;
230         kfree(keys);
231         kfree(p->tcfp_keys_ex);
232 }
233
234 static bool offset_valid(struct sk_buff *skb, int offset)
235 {
236         if (offset > 0 && offset > skb->len)
237                 return false;
238
239         if  (offset < 0 && -offset > skb_headroom(skb))
240                 return false;
241
242         return true;
243 }
244
245 static int pedit_skb_hdr_offset(struct sk_buff *skb,
246                                 enum pedit_header_type htype, int *hoffset)
247 {
248         int ret = -EINVAL;
249
250         switch (htype) {
251         case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
252                 if (skb_mac_header_was_set(skb)) {
253                         *hoffset = skb_mac_offset(skb);
254                         ret = 0;
255                 }
256                 break;
257         case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK:
258         case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
259         case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
260                 *hoffset = skb_network_offset(skb);
261                 ret = 0;
262                 break;
263         case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
264         case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
265                 if (skb_transport_header_was_set(skb)) {
266                         *hoffset = skb_transport_offset(skb);
267                         ret = 0;
268                 }
269                 break;
270         default:
271                 ret = -EINVAL;
272                 break;
273         };
274
275         return ret;
276 }
277
278 static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a,
279                      struct tcf_result *res)
280 {
281         struct tcf_pedit *p = to_pedit(a);
282         int i;
283
284         if (skb_unclone(skb, GFP_ATOMIC))
285                 return p->tcf_action;
286
287         spin_lock(&p->tcf_lock);
288
289         tcf_lastuse_update(&p->tcf_tm);
290
291         if (p->tcfp_nkeys > 0) {
292                 struct tc_pedit_key *tkey = p->tcfp_keys;
293                 struct tcf_pedit_key_ex *tkey_ex = p->tcfp_keys_ex;
294                 enum pedit_header_type htype = TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
295                 enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET;
296
297                 for (i = p->tcfp_nkeys; i > 0; i--, tkey++) {
298                         u32 *ptr, _data;
299                         int offset = tkey->off;
300                         int hoffset;
301                         u32 val;
302                         int rc;
303
304                         if (tkey_ex) {
305                                 htype = tkey_ex->htype;
306                                 cmd = tkey_ex->cmd;
307
308                                 tkey_ex++;
309                         }
310
311                         rc = pedit_skb_hdr_offset(skb, htype, &hoffset);
312                         if (rc) {
313                                 pr_info("tc filter pedit bad header type specified (0x%x)\n",
314                                         htype);
315                                 goto bad;
316                         }
317
318                         if (tkey->offmask) {
319                                 char *d, _d;
320
321                                 if (!offset_valid(skb, hoffset + tkey->at)) {
322                                         pr_info("tc filter pedit 'at' offset %d out of bounds\n",
323                                                 hoffset + tkey->at);
324                                         goto bad;
325                                 }
326                                 d = skb_header_pointer(skb, hoffset + tkey->at, 1,
327                                                        &_d);
328                                 if (!d)
329                                         goto bad;
330                                 offset += (*d & tkey->offmask) >> tkey->shift;
331                         }
332
333                         if (offset % 4) {
334                                 pr_info("tc filter pedit"
335                                         " offset must be on 32 bit boundaries\n");
336                                 goto bad;
337                         }
338
339                         if (!offset_valid(skb, hoffset + offset)) {
340                                 pr_info("tc filter pedit offset %d out of bounds\n",
341                                         hoffset + offset);
342                                 goto bad;
343                         }
344
345                         ptr = skb_header_pointer(skb, hoffset + offset, 4, &_data);
346                         if (!ptr)
347                                 goto bad;
348                         /* just do it, baby */
349                         switch (cmd) {
350                         case TCA_PEDIT_KEY_EX_CMD_SET:
351                                 val = tkey->val;
352                                 break;
353                         case TCA_PEDIT_KEY_EX_CMD_ADD:
354                                 val = (*ptr + tkey->val) & ~tkey->mask;
355                                 break;
356                         default:
357                                 pr_info("tc filter pedit bad command (%d)\n",
358                                         cmd);
359                                 goto bad;
360                         }
361
362                         *ptr = ((*ptr & tkey->mask) ^ val);
363                         if (ptr == &_data)
364                                 skb_store_bits(skb, hoffset + offset, ptr, 4);
365                 }
366
367                 goto done;
368         } else
369                 WARN(1, "pedit BUG: index %d\n", p->tcf_index);
370
371 bad:
372         p->tcf_qstats.overlimits++;
373 done:
374         bstats_update(&p->tcf_bstats, skb);
375         spin_unlock(&p->tcf_lock);
376         return p->tcf_action;
377 }
378
379 static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a,
380                           int bind, int ref)
381 {
382         unsigned char *b = skb_tail_pointer(skb);
383         struct tcf_pedit *p = to_pedit(a);
384         struct tc_pedit *opt;
385         struct tcf_t t;
386         int s;
387
388         s = sizeof(*opt) + p->tcfp_nkeys * sizeof(struct tc_pedit_key);
389
390         /* netlink spinlocks held above us - must use ATOMIC */
391         opt = kzalloc(s, GFP_ATOMIC);
392         if (unlikely(!opt))
393                 return -ENOBUFS;
394
395         memcpy(opt->keys, p->tcfp_keys,
396                p->tcfp_nkeys * sizeof(struct tc_pedit_key));
397         opt->index = p->tcf_index;
398         opt->nkeys = p->tcfp_nkeys;
399         opt->flags = p->tcfp_flags;
400         opt->action = p->tcf_action;
401         opt->refcnt = p->tcf_refcnt - ref;
402         opt->bindcnt = p->tcf_bindcnt - bind;
403
404         if (p->tcfp_keys_ex) {
405                 if (tcf_pedit_key_ex_dump(skb,
406                                           p->tcfp_keys_ex,
407                                           p->tcfp_nkeys))
408                         goto nla_put_failure;
409
410                 if (nla_put(skb, TCA_PEDIT_PARMS_EX, s, opt))
411                         goto nla_put_failure;
412         } else {
413                 if (nla_put(skb, TCA_PEDIT_PARMS, s, opt))
414                         goto nla_put_failure;
415         }
416
417         tcf_tm_dump(&t, &p->tcf_tm);
418         if (nla_put_64bit(skb, TCA_PEDIT_TM, sizeof(t), &t, TCA_PEDIT_PAD))
419                 goto nla_put_failure;
420
421         kfree(opt);
422         return skb->len;
423
424 nla_put_failure:
425         nlmsg_trim(skb, b);
426         kfree(opt);
427         return -1;
428 }
429
430 static int tcf_pedit_walker(struct net *net, struct sk_buff *skb,
431                             struct netlink_callback *cb, int type,
432                             const struct tc_action_ops *ops)
433 {
434         struct tc_action_net *tn = net_generic(net, pedit_net_id);
435
436         return tcf_generic_walker(tn, skb, cb, type, ops);
437 }
438
439 static int tcf_pedit_search(struct net *net, struct tc_action **a, u32 index)
440 {
441         struct tc_action_net *tn = net_generic(net, pedit_net_id);
442
443         return tcf_idr_search(tn, a, index);
444 }
445
446 static struct tc_action_ops act_pedit_ops = {
447         .kind           =       "pedit",
448         .type           =       TCA_ACT_PEDIT,
449         .owner          =       THIS_MODULE,
450         .act            =       tcf_pedit,
451         .dump           =       tcf_pedit_dump,
452         .cleanup        =       tcf_pedit_cleanup,
453         .init           =       tcf_pedit_init,
454         .walk           =       tcf_pedit_walker,
455         .lookup         =       tcf_pedit_search,
456         .size           =       sizeof(struct tcf_pedit),
457 };
458
459 static __net_init int pedit_init_net(struct net *net)
460 {
461         struct tc_action_net *tn = net_generic(net, pedit_net_id);
462
463         return tc_action_net_init(net, tn, &act_pedit_ops);
464 }
465
466 static void __net_exit pedit_exit_net(struct net *net)
467 {
468         struct tc_action_net *tn = net_generic(net, pedit_net_id);
469
470         tc_action_net_exit(tn);
471 }
472
473 static struct pernet_operations pedit_net_ops = {
474         .init = pedit_init_net,
475         .exit = pedit_exit_net,
476         .id   = &pedit_net_id,
477         .size = sizeof(struct tc_action_net),
478 };
479
480 MODULE_AUTHOR("Jamal Hadi Salim(2002-4)");
481 MODULE_DESCRIPTION("Generic Packet Editor actions");
482 MODULE_LICENSE("GPL");
483
484 static int __init pedit_init_module(void)
485 {
486         return tcf_register_action(&act_pedit_ops, &pedit_net_ops);
487 }
488
489 static void __exit pedit_cleanup_module(void)
490 {
491         tcf_unregister_action(&act_pedit_ops, &pedit_net_ops);
492 }
493
494 module_init(pedit_init_module);
495 module_exit(pedit_cleanup_module);
496