Blame view

net/sched/act_pedit.c 12 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
  /*
0c6965dd3   Jiri Pirko   sched: fix act fi...
3
   * net/sched/act_pedit.c	Generic packet editor
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
   * Authors:	Jamal Hadi Salim (2002-4)
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
8
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
  #include <linux/string.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
13
14
  #include <linux/skbuff.h>
  #include <linux/rtnetlink.h>
  #include <linux/module.h>
  #include <linux/init.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
15
  #include <linux/slab.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
16
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
  #include <net/pkt_sched.h>
  #include <linux/tc_act/tc_pedit.h>
  #include <net/tc_act/tc_pedit.h>
71d0ed707   Amir Vadai   net/act_pedit: Su...
20
  #include <uapi/linux/tc_act/tc_pedit.h>
6ac86ca35   Davide Caratti   net/sched: act_pe...
21
  #include <net/pkt_cls.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22

c7d03a00b   Alexey Dobriyan   netns: make struc...
23
  static unsigned int pedit_net_id;
a85a970af   WANG Cong   net_sched: move t...
24
  static struct tc_action_ops act_pedit_ops;
ddf97ccdd   WANG Cong   net_sched: add ne...
25

53b2bf3f8   Patrick McHardy   [NET_SCHED]: Use ...
26
  static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = {
53f7e35f8   jamal   pkt_sched: pedit ...
27
  	[TCA_PEDIT_PARMS]	= { .len = sizeof(struct tc_pedit) },
71d0ed707   Amir Vadai   net/act_pedit: Su...
28
  	[TCA_PEDIT_KEYS_EX]   = { .type = NLA_NESTED },
53b2bf3f8   Patrick McHardy   [NET_SCHED]: Use ...
29
  };
71d0ed707   Amir Vadai   net/act_pedit: Su...
30
31
  static const struct nla_policy pedit_key_ex_policy[TCA_PEDIT_KEY_EX_MAX + 1] = {
  	[TCA_PEDIT_KEY_EX_HTYPE]  = { .type = NLA_U16 },
853a14ba4   Amir Vadai   net/act_pedit: In...
32
  	[TCA_PEDIT_KEY_EX_CMD]	  = { .type = NLA_U16 },
71d0ed707   Amir Vadai   net/act_pedit: Su...
33
34
35
36
37
38
39
40
41
42
  };
  
  static struct tcf_pedit_key_ex *tcf_pedit_keys_ex_parse(struct nlattr *nla,
  							u8 n)
  {
  	struct tcf_pedit_key_ex *keys_ex;
  	struct tcf_pedit_key_ex *k;
  	const struct nlattr *ka;
  	int err = -EINVAL;
  	int rem;
f67169fef   Davide Caratti   net/sched: act_pe...
43
  	if (!nla)
71d0ed707   Amir Vadai   net/act_pedit: Su...
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  		return NULL;
  
  	keys_ex = kcalloc(n, sizeof(*k), GFP_KERNEL);
  	if (!keys_ex)
  		return ERR_PTR(-ENOMEM);
  
  	k = keys_ex;
  
  	nla_for_each_nested(ka, nla, rem) {
  		struct nlattr *tb[TCA_PEDIT_KEY_EX_MAX + 1];
  
  		if (!n) {
  			err = -EINVAL;
  			goto err_out;
  		}
  		n--;
  
  		if (nla_type(ka) != TCA_PEDIT_KEY_EX) {
  			err = -EINVAL;
  			goto err_out;
  		}
8cb081746   Johannes Berg   netlink: make val...
65
66
67
  		err = nla_parse_nested_deprecated(tb, TCA_PEDIT_KEY_EX_MAX,
  						  ka, pedit_key_ex_policy,
  						  NULL);
71d0ed707   Amir Vadai   net/act_pedit: Su...
68
69
  		if (err)
  			goto err_out;
853a14ba4   Amir Vadai   net/act_pedit: In...
70
71
  		if (!tb[TCA_PEDIT_KEY_EX_HTYPE] ||
  		    !tb[TCA_PEDIT_KEY_EX_CMD]) {
71d0ed707   Amir Vadai   net/act_pedit: Su...
72
73
74
75
76
  			err = -EINVAL;
  			goto err_out;
  		}
  
  		k->htype = nla_get_u16(tb[TCA_PEDIT_KEY_EX_HTYPE]);
853a14ba4   Amir Vadai   net/act_pedit: In...
77
  		k->cmd = nla_get_u16(tb[TCA_PEDIT_KEY_EX_CMD]);
71d0ed707   Amir Vadai   net/act_pedit: Su...
78

853a14ba4   Amir Vadai   net/act_pedit: In...
79
80
  		if (k->htype > TCA_PEDIT_HDR_TYPE_MAX ||
  		    k->cmd > TCA_PEDIT_CMD_MAX) {
71d0ed707   Amir Vadai   net/act_pedit: Su...
81
82
83
84
85
86
  			err = -EINVAL;
  			goto err_out;
  		}
  
  		k++;
  	}
c4f65b09b   Dan Carpenter   net/act_pedit: fi...
87
88
  	if (n) {
  		err = -EINVAL;
71d0ed707   Amir Vadai   net/act_pedit: Su...
89
  		goto err_out;
c4f65b09b   Dan Carpenter   net/act_pedit: fi...
90
  	}
71d0ed707   Amir Vadai   net/act_pedit: Su...
91
92
93
94
95
96
97
98
99
100
101
  
  	return keys_ex;
  
  err_out:
  	kfree(keys_ex);
  	return ERR_PTR(err);
  }
  
  static int tcf_pedit_key_ex_dump(struct sk_buff *skb,
  				 struct tcf_pedit_key_ex *keys_ex, int n)
  {
ae0be8de9   Michal Kubecek   netlink: make nla...
102
103
  	struct nlattr *keys_start = nla_nest_start_noflag(skb,
  							  TCA_PEDIT_KEYS_EX);
71d0ed707   Amir Vadai   net/act_pedit: Su...
104

85eb9af18   Davide Caratti   net/sched: act_pe...
105
106
  	if (!keys_start)
  		goto nla_failure;
71d0ed707   Amir Vadai   net/act_pedit: Su...
107
108
  	for (; n > 0; n--) {
  		struct nlattr *key_start;
ae0be8de9   Michal Kubecek   netlink: make nla...
109
  		key_start = nla_nest_start_noflag(skb, TCA_PEDIT_KEY_EX);
85eb9af18   Davide Caratti   net/sched: act_pe...
110
111
  		if (!key_start)
  			goto nla_failure;
71d0ed707   Amir Vadai   net/act_pedit: Su...
112

853a14ba4   Amir Vadai   net/act_pedit: In...
113
  		if (nla_put_u16(skb, TCA_PEDIT_KEY_EX_HTYPE, keys_ex->htype) ||
85eb9af18   Davide Caratti   net/sched: act_pe...
114
115
  		    nla_put_u16(skb, TCA_PEDIT_KEY_EX_CMD, keys_ex->cmd))
  			goto nla_failure;
71d0ed707   Amir Vadai   net/act_pedit: Su...
116
117
118
119
120
121
122
123
124
  
  		nla_nest_end(skb, key_start);
  
  		keys_ex++;
  	}
  
  	nla_nest_end(skb, keys_start);
  
  	return 0;
85eb9af18   Davide Caratti   net/sched: act_pe...
125
126
127
  nla_failure:
  	nla_nest_cancel(skb, keys_start);
  	return -EINVAL;
71d0ed707   Amir Vadai   net/act_pedit: Su...
128
  }
c1b52739e   Benjamin LaHaise   pkt_sched: namesp...
129
  static int tcf_pedit_init(struct net *net, struct nlattr *nla,
a85a970af   WANG Cong   net_sched: move t...
130
  			  struct nlattr *est, struct tc_action **a,
789871bb2   Vlad Buslov   net: sched: imple...
131
  			  int ovr, int bind, bool rtnl_held,
abbb0d336   Vlad Buslov   net: sched: exten...
132
133
  			  struct tcf_proto *tp, u32 flags,
  			  struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
135
  	struct tc_action_net *tn = net_generic(net, pedit_net_id);
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
136
  	struct nlattr *tb[TCA_PEDIT_MAX + 1];
6ac86ca35   Davide Caratti   net/sched: act_pe...
137
  	struct tcf_chain *goto_ch = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  	struct tc_pedit_key *keys = NULL;
71d0ed707   Amir Vadai   net/act_pedit: Su...
139
  	struct tcf_pedit_key_ex *keys_ex;
80f0f574c   Roman Mashak   net sched actions...
140
141
142
143
  	struct tc_pedit *parm;
  	struct nlattr *pattr;
  	struct tcf_pedit *p;
  	int ret = 0, err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
  	int ksize;
7be8ef2cd   Dmytro Linkin   net: sched: use t...
145
  	u32 index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146

9868c0b2e   Roman Mashak   net sched actions...
147
148
  	if (!nla) {
  		NL_SET_ERR_MSG_MOD(extack, "Pedit requires attributes to be passed");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
  		return -EINVAL;
9868c0b2e   Roman Mashak   net sched actions...
150
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151

8cb081746   Johannes Berg   netlink: make val...
152
153
  	err = nla_parse_nested_deprecated(tb, TCA_PEDIT_MAX, nla,
  					  pedit_policy, NULL);
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
154
155
  	if (err < 0)
  		return err;
71d0ed707   Amir Vadai   net/act_pedit: Su...
156
157
158
  	pattr = tb[TCA_PEDIT_PARMS];
  	if (!pattr)
  		pattr = tb[TCA_PEDIT_PARMS_EX];
9868c0b2e   Roman Mashak   net sched actions...
159
160
  	if (!pattr) {
  		NL_SET_ERR_MSG_MOD(extack, "Missing required TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
  		return -EINVAL;
9868c0b2e   Roman Mashak   net sched actions...
162
  	}
71d0ed707   Amir Vadai   net/act_pedit: Su...
163
164
  
  	parm = nla_data(pattr);
f67169fef   Davide Caratti   net/sched: act_pe...
165
166
167
168
  	if (!parm->nkeys) {
  		NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed");
  		return -EINVAL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
  	ksize = parm->nkeys * sizeof(struct tc_pedit_key);
9868c0b2e   Roman Mashak   net sched actions...
170
171
  	if (nla_len(pattr) < sizeof(*parm) + ksize) {
  		NL_SET_ERR_MSG_ATTR(extack, pattr, "Length of TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute is invalid");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
  		return -EINVAL;
9868c0b2e   Roman Mashak   net sched actions...
173
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174

71d0ed707   Amir Vadai   net/act_pedit: Su...
175
176
177
  	keys_ex = tcf_pedit_keys_ex_parse(tb[TCA_PEDIT_KEYS_EX], parm->nkeys);
  	if (IS_ERR(keys_ex))
  		return PTR_ERR(keys_ex);
7be8ef2cd   Dmytro Linkin   net: sched: use t...
178
179
  	index = parm->index;
  	err = tcf_idr_check_alloc(tn, &index, a, bind);
0190c1d45   Vlad Buslov   net: sched: atomi...
180
  	if (!err) {
7be8ef2cd   Dmytro Linkin   net: sched: use t...
181
  		ret = tcf_idr_create(tn, index, est, a,
e38226786   Vlad Buslov   net: sched: updat...
182
  				     &act_pedit_ops, bind, false, 0);
0190c1d45   Vlad Buslov   net: sched: atomi...
183
  		if (ret) {
7be8ef2cd   Dmytro Linkin   net: sched: use t...
184
  			tcf_idr_cleanup(tn, index);
30e99ed6d   Wei Yongjun   net: sched: act_p...
185
  			goto out_free;
0190c1d45   Vlad Buslov   net: sched: atomi...
186
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
  		ret = ACT_P_CREATED;
0190c1d45   Vlad Buslov   net: sched: atomi...
188
  	} else if (err > 0) {
1a29321ed   Jamal Hadi Salim   net_sched: act: D...
189
  		if (bind)
30e99ed6d   Wei Yongjun   net: sched: act_p...
190
  			goto out_free;
30e99ed6d   Wei Yongjun   net: sched: act_p...
191
192
  		if (!ovr) {
  			ret = -EEXIST;
67b0c1a3c   Vlad Buslov   net: sched: act_p...
193
  			goto out_release;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
  		}
0190c1d45   Vlad Buslov   net: sched: atomi...
195
  	} else {
19ab69107   Davide Caratti   net/sched: act_pe...
196
197
  		ret = err;
  		goto out_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
  	}
6ac86ca35   Davide Caratti   net/sched: act_pe...
199
200
201
202
203
  	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
  	if (err < 0) {
  		ret = err;
  		goto out_release;
  	}
67b0c1a3c   Vlad Buslov   net: sched: act_p...
204
  	p = to_pedit(*a);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
205
  	spin_lock_bh(&p->tcf_lock);
67b0c1a3c   Vlad Buslov   net: sched: act_p...
206
207
208
209
210
211
212
  
  	if (ret == ACT_P_CREATED ||
  	    (p->tcfp_nkeys && p->tcfp_nkeys != parm->nkeys)) {
  		keys = kmalloc(ksize, GFP_ATOMIC);
  		if (!keys) {
  			spin_unlock_bh(&p->tcf_lock);
  			ret = -ENOMEM;
6ac86ca35   Davide Caratti   net/sched: act_pe...
213
  			goto put_chain;
67b0c1a3c   Vlad Buslov   net: sched: act_p...
214
  		}
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
215
216
217
  		kfree(p->tcfp_keys);
  		p->tcfp_keys = keys;
  		p->tcfp_nkeys = parm->nkeys;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
  	}
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
219
  	memcpy(p->tcfp_keys, parm->keys, ksize);
71d0ed707   Amir Vadai   net/act_pedit: Su...
220

67b0c1a3c   Vlad Buslov   net: sched: act_p...
221
  	p->tcfp_flags = parm->flags;
6ac86ca35   Davide Caratti   net/sched: act_pe...
222
  	goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
67b0c1a3c   Vlad Buslov   net: sched: act_p...
223

71d0ed707   Amir Vadai   net/act_pedit: Su...
224
225
  	kfree(p->tcfp_keys_ex);
  	p->tcfp_keys_ex = keys_ex;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
226
  	spin_unlock_bh(&p->tcf_lock);
6ac86ca35   Davide Caratti   net/sched: act_pe...
227
228
  	if (goto_ch)
  		tcf_chain_put_by_act(goto_ch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
  	return ret;
67b0c1a3c   Vlad Buslov   net: sched: act_p...
230

6ac86ca35   Davide Caratti   net/sched: act_pe...
231
232
233
  put_chain:
  	if (goto_ch)
  		tcf_chain_put_by_act(goto_ch);
67b0c1a3c   Vlad Buslov   net: sched: act_p...
234
235
  out_release:
  	tcf_idr_release(*a, bind);
30e99ed6d   Wei Yongjun   net: sched: act_p...
236
237
238
  out_free:
  	kfree(keys_ex);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
  }
9a63b255d   Cong Wang   net_sched: remove...
240
  static void tcf_pedit_cleanup(struct tc_action *a)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
  {
a85a970af   WANG Cong   net_sched: move t...
242
  	struct tcf_pedit *p = to_pedit(a);
a5b5c958f   WANG Cong   net_sched: act: r...
243
  	struct tc_pedit_key *keys = p->tcfp_keys;
80f0f574c   Roman Mashak   net sched actions...
244

a5b5c958f   WANG Cong   net_sched: act: r...
245
  	kfree(keys);
71d0ed707   Amir Vadai   net/act_pedit: Su...
246
  	kfree(p->tcfp_keys_ex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
  }
95c2027bf   Amir Vadai   net/sched: pedit:...
248
249
250
251
252
253
254
255
256
257
  static bool offset_valid(struct sk_buff *skb, int offset)
  {
  	if (offset > 0 && offset > skb->len)
  		return false;
  
  	if  (offset < 0 && -offset > skb_headroom(skb))
  		return false;
  
  	return true;
  }
71d0ed707   Amir Vadai   net/act_pedit: Su...
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
  static int pedit_skb_hdr_offset(struct sk_buff *skb,
  				enum pedit_header_type htype, int *hoffset)
  {
  	int ret = -EINVAL;
  
  	switch (htype) {
  	case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
  		if (skb_mac_header_was_set(skb)) {
  			*hoffset = skb_mac_offset(skb);
  			ret = 0;
  		}
  		break;
  	case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK:
  	case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
  	case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
  		*hoffset = skb_network_offset(skb);
  		ret = 0;
  		break;
  	case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
  	case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
  		if (skb_transport_header_was_set(skb)) {
  			*hoffset = skb_transport_offset(skb);
  			ret = 0;
  		}
  		break;
  	default:
  		ret = -EINVAL;
  		break;
0a80848ec   YueHaibing   act_pedit: remove...
286
  	}
71d0ed707   Amir Vadai   net/act_pedit: Su...
287
288
289
  
  	return ret;
  }
6a2b401cd   Jamal Hadi Salim   net: sched: act_p...
290
291
  static int tcf_pedit_act(struct sk_buff *skb, const struct tc_action *a,
  			 struct tcf_result *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
  {
a85a970af   WANG Cong   net_sched: move t...
293
  	struct tcf_pedit *p = to_pedit(a);
4749c3ef8   Florian Westphal   net: sched: remov...
294
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295

14bbd6a56   Pravin B Shelar   net: Add skb_uncl...
296
  	if (skb_unclone(skb, GFP_ATOMIC))
cc7ec456f   Eric Dumazet   net_sched: cleanups
297
  		return p->tcf_action;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298

e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
299
  	spin_lock(&p->tcf_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300

9c4a4e488   Jamal Hadi Salim   net sched: action...
301
  	tcf_lastuse_update(&p->tcf_tm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302

e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
303
304
  	if (p->tcfp_nkeys > 0) {
  		struct tc_pedit_key *tkey = p->tcfp_keys;
71d0ed707   Amir Vadai   net/act_pedit: Su...
305
  		struct tcf_pedit_key_ex *tkey_ex = p->tcfp_keys_ex;
80f0f574c   Roman Mashak   net sched actions...
306
307
  		enum pedit_header_type htype =
  			TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
853a14ba4   Amir Vadai   net/act_pedit: In...
308
  		enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309

e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
310
  		for (i = p->tcfp_nkeys; i > 0; i--, tkey++) {
544377cd2   Roman Mashak   net sched actions...
311
  			u32 *ptr, hdata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
  			int offset = tkey->off;
71d0ed707   Amir Vadai   net/act_pedit: Su...
313
  			int hoffset;
853a14ba4   Amir Vadai   net/act_pedit: In...
314
  			u32 val;
71d0ed707   Amir Vadai   net/act_pedit: Su...
315
316
317
318
  			int rc;
  
  			if (tkey_ex) {
  				htype = tkey_ex->htype;
853a14ba4   Amir Vadai   net/act_pedit: In...
319
  				cmd = tkey_ex->cmd;
71d0ed707   Amir Vadai   net/act_pedit: Su...
320
321
322
323
324
  				tkey_ex++;
  			}
  
  			rc = pedit_skb_hdr_offset(skb, htype, &hoffset);
  			if (rc) {
95b0d2dc1   Roman Mashak   net sched actions...
325
326
  				pr_info("tc action pedit bad header type specified (0x%x)
  ",
71d0ed707   Amir Vadai   net/act_pedit: Su...
327
328
329
  					htype);
  				goto bad;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
  
  			if (tkey->offmask) {
430527415   Roman Mashak   net sched actions...
332
  				u8 *d, _d;
db2c24175   Changli Gao   act_pedit: access...
333

71d0ed707   Amir Vadai   net/act_pedit: Su...
334
  				if (!offset_valid(skb, hoffset + tkey->at)) {
95b0d2dc1   Roman Mashak   net sched actions...
335
336
  					pr_info("tc action pedit 'at' offset %d out of bounds
  ",
71d0ed707   Amir Vadai   net/act_pedit: Su...
337
  						hoffset + tkey->at);
95c2027bf   Amir Vadai   net/sched: pedit:...
338
339
  					goto bad;
  				}
80f0f574c   Roman Mashak   net sched actions...
340
  				d = skb_header_pointer(skb, hoffset + tkey->at,
6ff7586e3   Roman Mashak   net sched actions...
341
  						       sizeof(_d), &_d);
db2c24175   Changli Gao   act_pedit: access...
342
  				if (!d)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
  					goto bad;
db2c24175   Changli Gao   act_pedit: access...
344
  				offset += (*d & tkey->offmask) >> tkey->shift;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345
346
347
  			}
  
  			if (offset % 4) {
95b0d2dc1   Roman Mashak   net sched actions...
348
349
  				pr_info("tc action pedit offset must be on 32 bit boundaries
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
351
  				goto bad;
  			}
95c2027bf   Amir Vadai   net/sched: pedit:...
352

71d0ed707   Amir Vadai   net/act_pedit: Su...
353
  			if (!offset_valid(skb, hoffset + offset)) {
95b0d2dc1   Roman Mashak   net sched actions...
354
355
  				pr_info("tc action pedit offset %d out of bounds
  ",
71d0ed707   Amir Vadai   net/act_pedit: Su...
356
  					hoffset + offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
358
  				goto bad;
  			}
80f0f574c   Roman Mashak   net sched actions...
359
  			ptr = skb_header_pointer(skb, hoffset + offset,
6ff7586e3   Roman Mashak   net sched actions...
360
  						 sizeof(hdata), &hdata);
db2c24175   Changli Gao   act_pedit: access...
361
362
  			if (!ptr)
  				goto bad;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
  			/* just do it, baby */
853a14ba4   Amir Vadai   net/act_pedit: In...
364
365
366
367
368
369
370
371
  			switch (cmd) {
  			case TCA_PEDIT_KEY_EX_CMD_SET:
  				val = tkey->val;
  				break;
  			case TCA_PEDIT_KEY_EX_CMD_ADD:
  				val = (*ptr + tkey->val) & ~tkey->mask;
  				break;
  			default:
95b0d2dc1   Roman Mashak   net sched actions...
372
373
  				pr_info("tc action pedit bad command (%d)
  ",
853a14ba4   Amir Vadai   net/act_pedit: In...
374
375
376
377
378
  					cmd);
  				goto bad;
  			}
  
  			*ptr = ((*ptr & tkey->mask) ^ val);
544377cd2   Roman Mashak   net sched actions...
379
  			if (ptr == &hdata)
71d0ed707   Amir Vadai   net/act_pedit: Su...
380
  				skb_store_bits(skb, hoffset + offset, ptr, 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
  		}
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
382

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
  		goto done;
80f0f574c   Roman Mashak   net sched actions...
384
  	} else {
6ff9c3644   stephen hemminger   net sched: printk...
385
386
  		WARN(1, "pedit BUG: index %d
  ", p->tcf_index);
80f0f574c   Roman Mashak   net sched actions...
387
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
389
  
  bad:
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
390
  	p->tcf_qstats.overlimits++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
  done:
bfe0d0298   Eric Dumazet   net_sched: factor...
392
  	bstats_update(&p->tcf_bstats, skb);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
393
394
  	spin_unlock(&p->tcf_lock);
  	return p->tcf_action;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
395
  }
4b61d3e8d   Po Liu   net: qos offload ...
396
397
  static void tcf_pedit_stats_update(struct tc_action *a, u64 bytes, u64 packets,
  				   u64 drops, u64 lastuse, bool hw)
d4d9d9c53   Petr Machata   sched: act_pedit:...
398
399
400
  {
  	struct tcf_pedit *d = to_pedit(a);
  	struct tcf_t *tm = &d->tcf_tm;
4b61d3e8d   Po Liu   net: qos offload ...
401
  	tcf_action_update_stats(a, bytes, packets, drops, hw);
d4d9d9c53   Petr Machata   sched: act_pedit:...
402
403
  	tm->lastuse = max_t(u64, tm->lastuse, lastuse);
  }
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
404
405
  static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a,
  			  int bind, int ref)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
  {
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
407
  	unsigned char *b = skb_tail_pointer(skb);
a85a970af   WANG Cong   net_sched: move t...
408
  	struct tcf_pedit *p = to_pedit(a);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
  	struct tc_pedit *opt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
  	struct tcf_t t;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
411
  	int s;
8fe5756c7   Gustavo A. R. Silva   net/sched: use st...
412
  	s = struct_size(opt, keys, p->tcfp_nkeys);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
  
  	/* netlink spinlocks held above us - must use ATOMIC */
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
415
  	opt = kzalloc(s, GFP_ATOMIC);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
416
  	if (unlikely(!opt))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
  		return -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418

67b0c1a3c   Vlad Buslov   net: sched: act_p...
419
  	spin_lock_bh(&p->tcf_lock);
a0d716d8e   Gustavo A. R. Silva   net/sched: act_pe...
420
  	memcpy(opt->keys, p->tcfp_keys, flex_array_size(opt, keys, p->tcfp_nkeys));
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
421
422
423
424
  	opt->index = p->tcf_index;
  	opt->nkeys = p->tcfp_nkeys;
  	opt->flags = p->tcfp_flags;
  	opt->action = p->tcf_action;
036bb4432   Vlad Buslov   net: sched: chang...
425
426
  	opt->refcnt = refcount_read(&p->tcf_refcnt) - ref;
  	opt->bindcnt = atomic_read(&p->tcf_bindcnt) - bind;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427

71d0ed707   Amir Vadai   net/act_pedit: Su...
428
  	if (p->tcfp_keys_ex) {
85eb9af18   Davide Caratti   net/sched: act_pe...
429
430
431
432
  		if (tcf_pedit_key_ex_dump(skb,
  					  p->tcfp_keys_ex,
  					  p->tcfp_nkeys))
  			goto nla_put_failure;
71d0ed707   Amir Vadai   net/act_pedit: Su...
433
434
435
436
437
438
439
  
  		if (nla_put(skb, TCA_PEDIT_PARMS_EX, s, opt))
  			goto nla_put_failure;
  	} else {
  		if (nla_put(skb, TCA_PEDIT_PARMS, s, opt))
  			goto nla_put_failure;
  	}
48d8ee169   Jamal Hadi Salim   net sched actions...
440
441
  
  	tcf_tm_dump(&t, &p->tcf_tm);
9854518ea   Nicolas Dichtel   sched: align nlat...
442
  	if (nla_put_64bit(skb, TCA_PEDIT_TM, sizeof(t), &t, TCA_PEDIT_PAD))
1b34ec43c   David S. Miller   pkt_sched: Stop u...
443
  		goto nla_put_failure;
67b0c1a3c   Vlad Buslov   net: sched: act_p...
444
  	spin_unlock_bh(&p->tcf_lock);
48d8ee169   Jamal Hadi Salim   net sched actions...
445

541673c85   Patrick McHardy   [PKT_SCHED]: Fix ...
446
  	kfree(opt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
  	return skb->len;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
448
  nla_put_failure:
67b0c1a3c   Vlad Buslov   net: sched: act_p...
449
  	spin_unlock_bh(&p->tcf_lock);
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
450
  	nlmsg_trim(skb, b);
541673c85   Patrick McHardy   [PKT_SCHED]: Fix ...
451
  	kfree(opt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452
453
  	return -1;
  }
ddf97ccdd   WANG Cong   net_sched: add ne...
454
455
  static int tcf_pedit_walker(struct net *net, struct sk_buff *skb,
  			    struct netlink_callback *cb, int type,
417801055   Alexander Aring   net: sched: act: ...
456
457
  			    const struct tc_action_ops *ops,
  			    struct netlink_ext_ack *extack)
ddf97ccdd   WANG Cong   net_sched: add ne...
458
459
  {
  	struct tc_action_net *tn = net_generic(net, pedit_net_id);
b36201455   Alexander Aring   net: sched: act: ...
460
  	return tcf_generic_walker(tn, skb, cb, type, ops, extack);
ddf97ccdd   WANG Cong   net_sched: add ne...
461
  }
f061b48c1   Cong Wang   Revert "net: sche...
462
  static int tcf_pedit_search(struct net *net, struct tc_action **a, u32 index)
ddf97ccdd   WANG Cong   net_sched: add ne...
463
464
  {
  	struct tc_action_net *tn = net_generic(net, pedit_net_id);
65a206c01   Chris Mi   net/sched: Change...
465
  	return tcf_idr_search(tn, a, index);
ddf97ccdd   WANG Cong   net_sched: add ne...
466
  }
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
467
  static struct tc_action_ops act_pedit_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
  	.kind		=	"pedit",
eddd2cf19   Eli Cohen   net: Change TCA_A...
469
  	.id		=	TCA_ID_PEDIT,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
470
  	.owner		=	THIS_MODULE,
6a2b401cd   Jamal Hadi Salim   net: sched: act_p...
471
  	.act		=	tcf_pedit_act,
d4d9d9c53   Petr Machata   sched: act_pedit:...
472
  	.stats_update	=	tcf_pedit_stats_update,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
  	.dump		=	tcf_pedit_dump,
  	.cleanup	=	tcf_pedit_cleanup,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
  	.init		=	tcf_pedit_init,
ddf97ccdd   WANG Cong   net_sched: add ne...
476
477
  	.walk		=	tcf_pedit_walker,
  	.lookup		=	tcf_pedit_search,
a85a970af   WANG Cong   net_sched: move t...
478
  	.size		=	sizeof(struct tcf_pedit),
ddf97ccdd   WANG Cong   net_sched: add ne...
479
480
481
482
483
  };
  
  static __net_init int pedit_init_net(struct net *net)
  {
  	struct tc_action_net *tn = net_generic(net, pedit_net_id);
981471bd3   Cong Wang   net_sched: fix a ...
484
  	return tc_action_net_init(net, tn, &act_pedit_ops);
ddf97ccdd   WANG Cong   net_sched: add ne...
485
  }
039af9c66   Cong Wang   net_sched: switch...
486
  static void __net_exit pedit_exit_net(struct list_head *net_list)
ddf97ccdd   WANG Cong   net_sched: add ne...
487
  {
039af9c66   Cong Wang   net_sched: switch...
488
  	tc_action_net_exit(net_list, pedit_net_id);
ddf97ccdd   WANG Cong   net_sched: add ne...
489
490
491
492
  }
  
  static struct pernet_operations pedit_net_ops = {
  	.init = pedit_init_net,
039af9c66   Cong Wang   net_sched: switch...
493
  	.exit_batch = pedit_exit_net,
ddf97ccdd   WANG Cong   net_sched: add ne...
494
495
  	.id   = &pedit_net_id,
  	.size = sizeof(struct tc_action_net),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
497
498
499
500
  };
  
  MODULE_AUTHOR("Jamal Hadi Salim(2002-4)");
  MODULE_DESCRIPTION("Generic Packet Editor actions");
  MODULE_LICENSE("GPL");
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
501
  static int __init pedit_init_module(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
503
  	return tcf_register_action(&act_pedit_ops, &pedit_net_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504
  }
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
505
  static void __exit pedit_cleanup_module(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
507
  	tcf_unregister_action(&act_pedit_ops, &pedit_net_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
509
510
511
  }
  
  module_init(pedit_init_module);
  module_exit(pedit_cleanup_module);