xref: /openbmc/linux/net/sched/act_pedit.c (revision 74ba9207)
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 #include <net/pkt_cls.h>
27 
28 static unsigned int pedit_net_id;
29 static struct tc_action_ops act_pedit_ops;
30 
31 static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = {
32 	[TCA_PEDIT_PARMS]	= { .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 || !n)
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_deprecated(tb, TCA_PEDIT_KEY_EX_MAX,
74 						  ka, pedit_key_ex_policy,
75 						  NULL);
76 		if (err)
77 			goto err_out;
78 
79 		if (!tb[TCA_PEDIT_KEY_EX_HTYPE] ||
80 		    !tb[TCA_PEDIT_KEY_EX_CMD]) {
81 			err = -EINVAL;
82 			goto err_out;
83 		}
84 
85 		k->htype = nla_get_u16(tb[TCA_PEDIT_KEY_EX_HTYPE]);
86 		k->cmd = nla_get_u16(tb[TCA_PEDIT_KEY_EX_CMD]);
87 
88 		if (k->htype > TCA_PEDIT_HDR_TYPE_MAX ||
89 		    k->cmd > TCA_PEDIT_CMD_MAX) {
90 			err = -EINVAL;
91 			goto err_out;
92 		}
93 
94 		k++;
95 	}
96 
97 	if (n) {
98 		err = -EINVAL;
99 		goto err_out;
100 	}
101 
102 	return keys_ex;
103 
104 err_out:
105 	kfree(keys_ex);
106 	return ERR_PTR(err);
107 }
108 
109 static int tcf_pedit_key_ex_dump(struct sk_buff *skb,
110 				 struct tcf_pedit_key_ex *keys_ex, int n)
111 {
112 	struct nlattr *keys_start = nla_nest_start_noflag(skb,
113 							  TCA_PEDIT_KEYS_EX);
114 
115 	if (!keys_start)
116 		goto nla_failure;
117 	for (; n > 0; n--) {
118 		struct nlattr *key_start;
119 
120 		key_start = nla_nest_start_noflag(skb, TCA_PEDIT_KEY_EX);
121 		if (!key_start)
122 			goto nla_failure;
123 
124 		if (nla_put_u16(skb, TCA_PEDIT_KEY_EX_HTYPE, keys_ex->htype) ||
125 		    nla_put_u16(skb, TCA_PEDIT_KEY_EX_CMD, keys_ex->cmd))
126 			goto nla_failure;
127 
128 		nla_nest_end(skb, key_start);
129 
130 		keys_ex++;
131 	}
132 
133 	nla_nest_end(skb, keys_start);
134 
135 	return 0;
136 nla_failure:
137 	nla_nest_cancel(skb, keys_start);
138 	return -EINVAL;
139 }
140 
141 static int tcf_pedit_init(struct net *net, struct nlattr *nla,
142 			  struct nlattr *est, struct tc_action **a,
143 			  int ovr, int bind, bool rtnl_held,
144 			  struct tcf_proto *tp, struct netlink_ext_ack *extack)
145 {
146 	struct tc_action_net *tn = net_generic(net, pedit_net_id);
147 	struct nlattr *tb[TCA_PEDIT_MAX + 1];
148 	struct tcf_chain *goto_ch = NULL;
149 	struct tc_pedit_key *keys = NULL;
150 	struct tcf_pedit_key_ex *keys_ex;
151 	struct tc_pedit *parm;
152 	struct nlattr *pattr;
153 	struct tcf_pedit *p;
154 	int ret = 0, err;
155 	int ksize;
156 
157 	if (!nla) {
158 		NL_SET_ERR_MSG_MOD(extack, "Pedit requires attributes to be passed");
159 		return -EINVAL;
160 	}
161 
162 	err = nla_parse_nested_deprecated(tb, TCA_PEDIT_MAX, nla,
163 					  pedit_policy, NULL);
164 	if (err < 0)
165 		return err;
166 
167 	pattr = tb[TCA_PEDIT_PARMS];
168 	if (!pattr)
169 		pattr = tb[TCA_PEDIT_PARMS_EX];
170 	if (!pattr) {
171 		NL_SET_ERR_MSG_MOD(extack, "Missing required TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute");
172 		return -EINVAL;
173 	}
174 
175 	parm = nla_data(pattr);
176 	ksize = parm->nkeys * sizeof(struct tc_pedit_key);
177 	if (nla_len(pattr) < sizeof(*parm) + ksize) {
178 		NL_SET_ERR_MSG_ATTR(extack, pattr, "Length of TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute is invalid");
179 		return -EINVAL;
180 	}
181 
182 	keys_ex = tcf_pedit_keys_ex_parse(tb[TCA_PEDIT_KEYS_EX], parm->nkeys);
183 	if (IS_ERR(keys_ex))
184 		return PTR_ERR(keys_ex);
185 
186 	err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
187 	if (!err) {
188 		if (!parm->nkeys) {
189 			tcf_idr_cleanup(tn, parm->index);
190 			NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed");
191 			ret = -EINVAL;
192 			goto out_free;
193 		}
194 		ret = tcf_idr_create(tn, parm->index, est, a,
195 				     &act_pedit_ops, bind, false);
196 		if (ret) {
197 			tcf_idr_cleanup(tn, parm->index);
198 			goto out_free;
199 		}
200 		ret = ACT_P_CREATED;
201 	} else if (err > 0) {
202 		if (bind)
203 			goto out_free;
204 		if (!ovr) {
205 			ret = -EEXIST;
206 			goto out_release;
207 		}
208 	} else {
209 		ret = err;
210 		goto out_free;
211 	}
212 
213 	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
214 	if (err < 0) {
215 		ret = err;
216 		goto out_release;
217 	}
218 	p = to_pedit(*a);
219 	spin_lock_bh(&p->tcf_lock);
220 
221 	if (ret == ACT_P_CREATED ||
222 	    (p->tcfp_nkeys && p->tcfp_nkeys != parm->nkeys)) {
223 		keys = kmalloc(ksize, GFP_ATOMIC);
224 		if (!keys) {
225 			spin_unlock_bh(&p->tcf_lock);
226 			ret = -ENOMEM;
227 			goto put_chain;
228 		}
229 		kfree(p->tcfp_keys);
230 		p->tcfp_keys = keys;
231 		p->tcfp_nkeys = parm->nkeys;
232 	}
233 	memcpy(p->tcfp_keys, parm->keys, ksize);
234 
235 	p->tcfp_flags = parm->flags;
236 	goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
237 
238 	kfree(p->tcfp_keys_ex);
239 	p->tcfp_keys_ex = keys_ex;
240 
241 	spin_unlock_bh(&p->tcf_lock);
242 	if (goto_ch)
243 		tcf_chain_put_by_act(goto_ch);
244 	if (ret == ACT_P_CREATED)
245 		tcf_idr_insert(tn, *a);
246 	return ret;
247 
248 put_chain:
249 	if (goto_ch)
250 		tcf_chain_put_by_act(goto_ch);
251 out_release:
252 	tcf_idr_release(*a, bind);
253 out_free:
254 	kfree(keys_ex);
255 	return ret;
256 
257 }
258 
259 static void tcf_pedit_cleanup(struct tc_action *a)
260 {
261 	struct tcf_pedit *p = to_pedit(a);
262 	struct tc_pedit_key *keys = p->tcfp_keys;
263 
264 	kfree(keys);
265 	kfree(p->tcfp_keys_ex);
266 }
267 
268 static bool offset_valid(struct sk_buff *skb, int offset)
269 {
270 	if (offset > 0 && offset > skb->len)
271 		return false;
272 
273 	if  (offset < 0 && -offset > skb_headroom(skb))
274 		return false;
275 
276 	return true;
277 }
278 
279 static int pedit_skb_hdr_offset(struct sk_buff *skb,
280 				enum pedit_header_type htype, int *hoffset)
281 {
282 	int ret = -EINVAL;
283 
284 	switch (htype) {
285 	case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
286 		if (skb_mac_header_was_set(skb)) {
287 			*hoffset = skb_mac_offset(skb);
288 			ret = 0;
289 		}
290 		break;
291 	case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK:
292 	case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
293 	case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
294 		*hoffset = skb_network_offset(skb);
295 		ret = 0;
296 		break;
297 	case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
298 	case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
299 		if (skb_transport_header_was_set(skb)) {
300 			*hoffset = skb_transport_offset(skb);
301 			ret = 0;
302 		}
303 		break;
304 	default:
305 		ret = -EINVAL;
306 		break;
307 	}
308 
309 	return ret;
310 }
311 
312 static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
313 			 struct tcf_result *res)
314 {
315 	struct tcf_pedit *p = to_pedit(a);
316 	int i;
317 
318 	if (skb_unclone(skb, GFP_ATOMIC))
319 		return p->tcf_action;
320 
321 	spin_lock(&p->tcf_lock);
322 
323 	tcf_lastuse_update(&p->tcf_tm);
324 
325 	if (p->tcfp_nkeys > 0) {
326 		struct tc_pedit_key *tkey = p->tcfp_keys;
327 		struct tcf_pedit_key_ex *tkey_ex = p->tcfp_keys_ex;
328 		enum pedit_header_type htype =
329 			TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
330 		enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET;
331 
332 		for (i = p->tcfp_nkeys; i > 0; i--, tkey++) {
333 			u32 *ptr, hdata;
334 			int offset = tkey->off;
335 			int hoffset;
336 			u32 val;
337 			int rc;
338 
339 			if (tkey_ex) {
340 				htype = tkey_ex->htype;
341 				cmd = tkey_ex->cmd;
342 
343 				tkey_ex++;
344 			}
345 
346 			rc = pedit_skb_hdr_offset(skb, htype, &hoffset);
347 			if (rc) {
348 				pr_info("tc action pedit bad header type specified (0x%x)\n",
349 					htype);
350 				goto bad;
351 			}
352 
353 			if (tkey->offmask) {
354 				u8 *d, _d;
355 
356 				if (!offset_valid(skb, hoffset + tkey->at)) {
357 					pr_info("tc action pedit 'at' offset %d out of bounds\n",
358 						hoffset + tkey->at);
359 					goto bad;
360 				}
361 				d = skb_header_pointer(skb, hoffset + tkey->at,
362 						       sizeof(_d), &_d);
363 				if (!d)
364 					goto bad;
365 				offset += (*d & tkey->offmask) >> tkey->shift;
366 			}
367 
368 			if (offset % 4) {
369 				pr_info("tc action pedit offset must be on 32 bit boundaries\n");
370 				goto bad;
371 			}
372 
373 			if (!offset_valid(skb, hoffset + offset)) {
374 				pr_info("tc action pedit offset %d out of bounds\n",
375 					hoffset + offset);
376 				goto bad;
377 			}
378 
379 			ptr = skb_header_pointer(skb, hoffset + offset,
380 						 sizeof(hdata), &hdata);
381 			if (!ptr)
382 				goto bad;
383 			/* just do it, baby */
384 			switch (cmd) {
385 			case TCA_PEDIT_KEY_EX_CMD_SET:
386 				val = tkey->val;
387 				break;
388 			case TCA_PEDIT_KEY_EX_CMD_ADD:
389 				val = (*ptr + tkey->val) & ~tkey->mask;
390 				break;
391 			default:
392 				pr_info("tc action pedit bad command (%d)\n",
393 					cmd);
394 				goto bad;
395 			}
396 
397 			*ptr = ((*ptr & tkey->mask) ^ val);
398 			if (ptr == &hdata)
399 				skb_store_bits(skb, hoffset + offset, ptr, 4);
400 		}
401 
402 		goto done;
403 	} else {
404 		WARN(1, "pedit BUG: index %d\n", p->tcf_index);
405 	}
406 
407 bad:
408 	p->tcf_qstats.overlimits++;
409 done:
410 	bstats_update(&p->tcf_bstats, skb);
411 	spin_unlock(&p->tcf_lock);
412 	return p->tcf_action;
413 }
414 
415 static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a,
416 			  int bind, int ref)
417 {
418 	unsigned char *b = skb_tail_pointer(skb);
419 	struct tcf_pedit *p = to_pedit(a);
420 	struct tc_pedit *opt;
421 	struct tcf_t t;
422 	int s;
423 
424 	s = struct_size(opt, keys, p->tcfp_nkeys);
425 
426 	/* netlink spinlocks held above us - must use ATOMIC */
427 	opt = kzalloc(s, GFP_ATOMIC);
428 	if (unlikely(!opt))
429 		return -ENOBUFS;
430 
431 	spin_lock_bh(&p->tcf_lock);
432 	memcpy(opt->keys, p->tcfp_keys,
433 	       p->tcfp_nkeys * sizeof(struct tc_pedit_key));
434 	opt->index = p->tcf_index;
435 	opt->nkeys = p->tcfp_nkeys;
436 	opt->flags = p->tcfp_flags;
437 	opt->action = p->tcf_action;
438 	opt->refcnt = refcount_read(&p->tcf_refcnt) - ref;
439 	opt->bindcnt = atomic_read(&p->tcf_bindcnt) - bind;
440 
441 	if (p->tcfp_keys_ex) {
442 		if (tcf_pedit_key_ex_dump(skb,
443 					  p->tcfp_keys_ex,
444 					  p->tcfp_nkeys))
445 			goto nla_put_failure;
446 
447 		if (nla_put(skb, TCA_PEDIT_PARMS_EX, s, opt))
448 			goto nla_put_failure;
449 	} else {
450 		if (nla_put(skb, TCA_PEDIT_PARMS, s, opt))
451 			goto nla_put_failure;
452 	}
453 
454 	tcf_tm_dump(&t, &p->tcf_tm);
455 	if (nla_put_64bit(skb, TCA_PEDIT_TM, sizeof(t), &t, TCA_PEDIT_PAD))
456 		goto nla_put_failure;
457 	spin_unlock_bh(&p->tcf_lock);
458 
459 	kfree(opt);
460 	return skb->len;
461 
462 nla_put_failure:
463 	spin_unlock_bh(&p->tcf_lock);
464 	nlmsg_trim(skb, b);
465 	kfree(opt);
466 	return -1;
467 }
468 
469 static int tcf_pedit_walker(struct net *net, struct sk_buff *skb,
470 			    struct netlink_callback *cb, int type,
471 			    const struct tc_action_ops *ops,
472 			    struct netlink_ext_ack *extack)
473 {
474 	struct tc_action_net *tn = net_generic(net, pedit_net_id);
475 
476 	return tcf_generic_walker(tn, skb, cb, type, ops, extack);
477 }
478 
479 static int tcf_pedit_search(struct net *net, struct tc_action **a, u32 index)
480 {
481 	struct tc_action_net *tn = net_generic(net, pedit_net_id);
482 
483 	return tcf_idr_search(tn, a, index);
484 }
485 
486 static struct tc_action_ops act_pedit_ops = {
487 	.kind		=	"pedit",
488 	.id		=	TCA_ID_PEDIT,
489 	.owner		=	THIS_MODULE,
490 	.act		=	tcf_pedit_act,
491 	.dump		=	tcf_pedit_dump,
492 	.cleanup	=	tcf_pedit_cleanup,
493 	.init		=	tcf_pedit_init,
494 	.walk		=	tcf_pedit_walker,
495 	.lookup		=	tcf_pedit_search,
496 	.size		=	sizeof(struct tcf_pedit),
497 };
498 
499 static __net_init int pedit_init_net(struct net *net)
500 {
501 	struct tc_action_net *tn = net_generic(net, pedit_net_id);
502 
503 	return tc_action_net_init(tn, &act_pedit_ops);
504 }
505 
506 static void __net_exit pedit_exit_net(struct list_head *net_list)
507 {
508 	tc_action_net_exit(net_list, pedit_net_id);
509 }
510 
511 static struct pernet_operations pedit_net_ops = {
512 	.init = pedit_init_net,
513 	.exit_batch = pedit_exit_net,
514 	.id   = &pedit_net_id,
515 	.size = sizeof(struct tc_action_net),
516 };
517 
518 MODULE_AUTHOR("Jamal Hadi Salim(2002-4)");
519 MODULE_DESCRIPTION("Generic Packet Editor actions");
520 MODULE_LICENSE("GPL");
521 
522 static int __init pedit_init_module(void)
523 {
524 	return tcf_register_action(&act_pedit_ops, &pedit_net_ops);
525 }
526 
527 static void __exit pedit_cleanup_module(void)
528 {
529 	tcf_unregister_action(&act_pedit_ops, &pedit_net_ops);
530 }
531 
532 module_init(pedit_init_module);
533 module_exit(pedit_cleanup_module);
534