Blame view

net/sched/act_pedit.c 10.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
0c6965dd3   Jiri Pirko   sched: fix act fi...
2
   * net/sched/act_pedit.c	Generic packet editor
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
6
7
8
9
10
   *
   *		This program is free software; you can redistribute it and/or
   *		modify it under the terms of the GNU General Public License
   *		as published by the Free Software Foundation; either version
   *		2 of the License, or (at your option) any later version.
   *
   * Authors:	Jamal Hadi Salim (2002-4)
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
  #include <linux/string.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
18
  #include <linux/skbuff.h>
  #include <linux/rtnetlink.h>
  #include <linux/module.h>
  #include <linux/init.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
19
  #include <linux/slab.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
20
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
23
  #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...
24
  #include <uapi/linux/tc_act/tc_pedit.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25

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

53b2bf3f8   Patrick McHardy   [NET_SCHED]: Use ...
29
  static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = {
53f7e35f8   jamal   pkt_sched: pedit ...
30
  	[TCA_PEDIT_PARMS]	= { .len = sizeof(struct tc_pedit) },
71d0ed707   Amir Vadai   net/act_pedit: Su...
31
  	[TCA_PEDIT_KEYS_EX]   = { .type = NLA_NESTED },
53b2bf3f8   Patrick McHardy   [NET_SCHED]: Use ...
32
  };
71d0ed707   Amir Vadai   net/act_pedit: Su...
33
34
  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...
35
  	[TCA_PEDIT_KEY_EX_CMD]	  = { .type = NLA_U16 },
71d0ed707   Amir Vadai   net/act_pedit: Su...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
  };
  
  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;
  
  	if (!nla || !n)
  		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;
  		}
  
  		err = nla_parse_nested(tb, TCA_PEDIT_KEY_EX_MAX, ka,
fceb6435e   Johannes Berg   netlink: pass ext...
71
  				       pedit_key_ex_policy, NULL);
71d0ed707   Amir Vadai   net/act_pedit: Su...
72
73
  		if (err)
  			goto err_out;
853a14ba4   Amir Vadai   net/act_pedit: In...
74
75
  		if (!tb[TCA_PEDIT_KEY_EX_HTYPE] ||
  		    !tb[TCA_PEDIT_KEY_EX_CMD]) {
71d0ed707   Amir Vadai   net/act_pedit: Su...
76
77
78
79
80
  			err = -EINVAL;
  			goto err_out;
  		}
  
  		k->htype = nla_get_u16(tb[TCA_PEDIT_KEY_EX_HTYPE]);
853a14ba4   Amir Vadai   net/act_pedit: In...
81
  		k->cmd = nla_get_u16(tb[TCA_PEDIT_KEY_EX_CMD]);
71d0ed707   Amir Vadai   net/act_pedit: Su...
82

853a14ba4   Amir Vadai   net/act_pedit: In...
83
84
  		if (k->htype > TCA_PEDIT_HDR_TYPE_MAX ||
  		    k->cmd > TCA_PEDIT_CMD_MAX) {
71d0ed707   Amir Vadai   net/act_pedit: Su...
85
86
87
88
89
90
  			err = -EINVAL;
  			goto err_out;
  		}
  
  		k++;
  	}
c4f65b09b   Dan Carpenter   net/act_pedit: fi...
91
92
  	if (n) {
  		err = -EINVAL;
71d0ed707   Amir Vadai   net/act_pedit: Su...
93
  		goto err_out;
c4f65b09b   Dan Carpenter   net/act_pedit: fi...
94
  	}
71d0ed707   Amir Vadai   net/act_pedit: Su...
95
96
97
98
99
100
101
102
103
104
105
106
  
  	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)
  {
  	struct nlattr *keys_start = nla_nest_start(skb, TCA_PEDIT_KEYS_EX);
417b068a6   Davide Caratti   net/sched: act_pe...
107
108
  	if (!keys_start)
  		goto nla_failure;
71d0ed707   Amir Vadai   net/act_pedit: Su...
109
110
111
112
  	for (; n > 0; n--) {
  		struct nlattr *key_start;
  
  		key_start = nla_nest_start(skb, TCA_PEDIT_KEY_EX);
417b068a6   Davide Caratti   net/sched: act_pe...
113
114
  		if (!key_start)
  			goto nla_failure;
71d0ed707   Amir Vadai   net/act_pedit: Su...
115

853a14ba4   Amir Vadai   net/act_pedit: In...
116
  		if (nla_put_u16(skb, TCA_PEDIT_KEY_EX_HTYPE, keys_ex->htype) ||
417b068a6   Davide Caratti   net/sched: act_pe...
117
118
  		    nla_put_u16(skb, TCA_PEDIT_KEY_EX_CMD, keys_ex->cmd))
  			goto nla_failure;
71d0ed707   Amir Vadai   net/act_pedit: Su...
119
120
121
122
123
124
125
126
127
  
  		nla_nest_end(skb, key_start);
  
  		keys_ex++;
  	}
  
  	nla_nest_end(skb, keys_start);
  
  	return 0;
417b068a6   Davide Caratti   net/sched: act_pe...
128
129
130
  nla_failure:
  	nla_nest_cancel(skb, keys_start);
  	return -EINVAL;
71d0ed707   Amir Vadai   net/act_pedit: Su...
131
  }
c1b52739e   Benjamin LaHaise   pkt_sched: namesp...
132
  static int tcf_pedit_init(struct net *net, struct nlattr *nla,
a85a970af   WANG Cong   net_sched: move t...
133
  			  struct nlattr *est, struct tc_action **a,
c1b52739e   Benjamin LaHaise   pkt_sched: namesp...
134
  			  int ovr, int bind)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
136
  	struct tc_action_net *tn = net_generic(net, pedit_net_id);
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
137
  	struct nlattr *tb[TCA_PEDIT_MAX + 1];
71d0ed707   Amir Vadai   net/act_pedit: Su...
138
  	struct nlattr *pattr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
  	struct tc_pedit *parm;
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
140
  	int ret = 0, err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
  	struct tcf_pedit *p;
  	struct tc_pedit_key *keys = NULL;
71d0ed707   Amir Vadai   net/act_pedit: Su...
143
  	struct tcf_pedit_key_ex *keys_ex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
  	int ksize;
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
145
  	if (nla == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
  		return -EINVAL;
fceb6435e   Johannes Berg   netlink: pass ext...
147
  	err = nla_parse_nested(tb, TCA_PEDIT_MAX, nla, pedit_policy, NULL);
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
148
149
  	if (err < 0)
  		return err;
71d0ed707   Amir Vadai   net/act_pedit: Su...
150
151
152
153
  	pattr = tb[TCA_PEDIT_PARMS];
  	if (!pattr)
  		pattr = tb[TCA_PEDIT_PARMS_EX];
  	if (!pattr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
  		return -EINVAL;
71d0ed707   Amir Vadai   net/act_pedit: Su...
155
156
  
  	parm = nla_data(pattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  	ksize = parm->nkeys * sizeof(struct tc_pedit_key);
71d0ed707   Amir Vadai   net/act_pedit: Su...
158
  	if (nla_len(pattr) < sizeof(*parm) + ksize)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
  		return -EINVAL;
71d0ed707   Amir Vadai   net/act_pedit: Su...
160
161
162
  	keys_ex = tcf_pedit_keys_ex_parse(tb[TCA_PEDIT_KEYS_EX], parm->nkeys);
  	if (IS_ERR(keys_ex))
  		return PTR_ERR(keys_ex);
65a206c01   Chris Mi   net/sched: Change...
163
  	if (!tcf_idr_check(tn, parm->index, a, bind)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
  		if (!parm->nkeys)
  			return -EINVAL;
65a206c01   Chris Mi   net/sched: Change...
166
167
  		ret = tcf_idr_create(tn, parm->index, est, a,
  				     &act_pedit_ops, bind, false);
86062033f   WANG Cong   net_sched: act: h...
168
169
  		if (ret)
  			return ret;
a85a970af   WANG Cong   net_sched: move t...
170
  		p = to_pedit(*a);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
  		keys = kmalloc(ksize, GFP_KERNEL);
  		if (keys == NULL) {
01a808396   Davide Caratti   net/sched: fix id...
173
  			tcf_idr_release(*a, bind);
71d0ed707   Amir Vadai   net/act_pedit: Su...
174
  			kfree(keys_ex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
177
178
  			return -ENOMEM;
  		}
  		ret = ACT_P_CREATED;
  	} else {
1a29321ed   Jamal Hadi Salim   net_sched: act: D...
179
180
  		if (bind)
  			return 0;
65a206c01   Chris Mi   net/sched: Change...
181
  		tcf_idr_release(*a, bind);
1a29321ed   Jamal Hadi Salim   net_sched: act: D...
182
  		if (!ovr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
  			return -EEXIST;
a85a970af   WANG Cong   net_sched: move t...
184
  		p = to_pedit(*a);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
185
  		if (p->tcfp_nkeys && p->tcfp_nkeys != parm->nkeys) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
  			keys = kmalloc(ksize, GFP_KERNEL);
71d0ed707   Amir Vadai   net/act_pedit: Su...
187
188
  			if (!keys) {
  				kfree(keys_ex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  				return -ENOMEM;
71d0ed707   Amir Vadai   net/act_pedit: Su...
190
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
  		}
  	}
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
193
194
195
  	spin_lock_bh(&p->tcf_lock);
  	p->tcfp_flags = parm->flags;
  	p->tcf_action = parm->action;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
  	if (keys) {
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
197
198
199
  		kfree(p->tcfp_keys);
  		p->tcfp_keys = keys;
  		p->tcfp_nkeys = parm->nkeys;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
  	}
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
201
  	memcpy(p->tcfp_keys, parm->keys, ksize);
71d0ed707   Amir Vadai   net/act_pedit: Su...
202
203
204
  
  	kfree(p->tcfp_keys_ex);
  	p->tcfp_keys_ex = keys_ex;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
205
  	spin_unlock_bh(&p->tcf_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
  	if (ret == ACT_P_CREATED)
65a206c01   Chris Mi   net/sched: Change...
207
  		tcf_idr_insert(tn, *a);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
  	return ret;
  }
a5b5c958f   WANG Cong   net_sched: act: r...
210
  static void tcf_pedit_cleanup(struct tc_action *a, int bind)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
  {
a85a970af   WANG Cong   net_sched: move t...
212
  	struct tcf_pedit *p = to_pedit(a);
a5b5c958f   WANG Cong   net_sched: act: r...
213
214
  	struct tc_pedit_key *keys = p->tcfp_keys;
  	kfree(keys);
71d0ed707   Amir Vadai   net/act_pedit: Su...
215
  	kfree(p->tcfp_keys_ex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
  }
95c2027bf   Amir Vadai   net/sched: pedit:...
217
218
219
220
221
222
223
224
225
226
  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...
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
  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;
  	};
  
  	return ret;
  }
dc7f9f6e8   Eric Dumazet   net: sched: const...
259
  static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a,
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
260
  		     struct tcf_result *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
  {
a85a970af   WANG Cong   net_sched: move t...
262
  	struct tcf_pedit *p = to_pedit(a);
4749c3ef8   Florian Westphal   net: sched: remov...
263
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264

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

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

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

e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
272
273
  	if (p->tcfp_nkeys > 0) {
  		struct tc_pedit_key *tkey = p->tcfp_keys;
71d0ed707   Amir Vadai   net/act_pedit: Su...
274
275
  		struct tcf_pedit_key_ex *tkey_ex = p->tcfp_keys_ex;
  		enum pedit_header_type htype = TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
853a14ba4   Amir Vadai   net/act_pedit: In...
276
  		enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277

e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
278
  		for (i = p->tcfp_nkeys; i > 0; i--, tkey++) {
db2c24175   Changli Gao   act_pedit: access...
279
  			u32 *ptr, _data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  			int offset = tkey->off;
71d0ed707   Amir Vadai   net/act_pedit: Su...
281
  			int hoffset;
853a14ba4   Amir Vadai   net/act_pedit: In...
282
  			u32 val;
71d0ed707   Amir Vadai   net/act_pedit: Su...
283
284
285
286
  			int rc;
  
  			if (tkey_ex) {
  				htype = tkey_ex->htype;
853a14ba4   Amir Vadai   net/act_pedit: In...
287
  				cmd = tkey_ex->cmd;
71d0ed707   Amir Vadai   net/act_pedit: Su...
288
289
290
291
292
293
294
295
296
297
  				tkey_ex++;
  			}
  
  			rc = pedit_skb_hdr_offset(skb, htype, &hoffset);
  			if (rc) {
  				pr_info("tc filter pedit bad header type specified (0x%x)
  ",
  					htype);
  				goto bad;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
  
  			if (tkey->offmask) {
db2c24175   Changli Gao   act_pedit: access...
300
  				char *d, _d;
71d0ed707   Amir Vadai   net/act_pedit: Su...
301
  				if (!offset_valid(skb, hoffset + tkey->at)) {
95c2027bf   Amir Vadai   net/sched: pedit:...
302
303
  					pr_info("tc filter pedit 'at' offset %d out of bounds
  ",
71d0ed707   Amir Vadai   net/act_pedit: Su...
304
  						hoffset + tkey->at);
95c2027bf   Amir Vadai   net/sched: pedit:...
305
306
  					goto bad;
  				}
71d0ed707   Amir Vadai   net/act_pedit: Su...
307
  				d = skb_header_pointer(skb, hoffset + tkey->at, 1,
db2c24175   Changli Gao   act_pedit: access...
308
309
  						       &_d);
  				if (!d)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
  					goto bad;
db2c24175   Changli Gao   act_pedit: access...
311
  				offset += (*d & tkey->offmask) >> tkey->shift;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
313
314
  			}
  
  			if (offset % 4) {
6ff9c3644   stephen hemminger   net sched: printk...
315
316
317
  				pr_info("tc filter pedit"
  					" offset must be on 32 bit boundaries
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
319
  				goto bad;
  			}
95c2027bf   Amir Vadai   net/sched: pedit:...
320

71d0ed707   Amir Vadai   net/act_pedit: Su...
321
  			if (!offset_valid(skb, hoffset + offset)) {
95c2027bf   Amir Vadai   net/sched: pedit:...
322
323
  				pr_info("tc filter pedit offset %d out of bounds
  ",
71d0ed707   Amir Vadai   net/act_pedit: Su...
324
  					hoffset + offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
326
  				goto bad;
  			}
71d0ed707   Amir Vadai   net/act_pedit: Su...
327
  			ptr = skb_header_pointer(skb, hoffset + offset, 4, &_data);
db2c24175   Changli Gao   act_pedit: access...
328
329
  			if (!ptr)
  				goto bad;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
  			/* just do it, baby */
853a14ba4   Amir Vadai   net/act_pedit: In...
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
  			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:
  				pr_info("tc filter pedit bad command (%d)
  ",
  					cmd);
  				goto bad;
  			}
  
  			*ptr = ((*ptr & tkey->mask) ^ val);
db2c24175   Changli Gao   act_pedit: access...
346
  			if (ptr == &_data)
71d0ed707   Amir Vadai   net/act_pedit: Su...
347
  				skb_store_bits(skb, hoffset + offset, ptr, 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
  		}
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
349

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
  		goto done;
6ff9c3644   stephen hemminger   net sched: printk...
351
352
353
  	} else
  		WARN(1, "pedit BUG: index %d
  ", p->tcf_index);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
  
  bad:
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
356
  	p->tcf_qstats.overlimits++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
  done:
bfe0d0298   Eric Dumazet   net_sched: factor...
358
  	bstats_update(&p->tcf_bstats, skb);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
359
360
  	spin_unlock(&p->tcf_lock);
  	return p->tcf_action;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
  }
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
362
363
  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
364
  {
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
365
  	unsigned char *b = skb_tail_pointer(skb);
a85a970af   WANG Cong   net_sched: move t...
366
  	struct tcf_pedit *p = to_pedit(a);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
  	struct tc_pedit *opt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
  	struct tcf_t t;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
369
  	int s;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
370
  	s = sizeof(*opt) + p->tcfp_nkeys * sizeof(struct tc_pedit_key);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
372
  
  	/* netlink spinlocks held above us - must use ATOMIC */
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
373
  	opt = kzalloc(s, GFP_ATOMIC);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
374
  	if (unlikely(!opt))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
  		return -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376

e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
377
378
379
380
381
382
383
384
  	memcpy(opt->keys, p->tcfp_keys,
  	       p->tcfp_nkeys * sizeof(struct tc_pedit_key));
  	opt->index = p->tcf_index;
  	opt->nkeys = p->tcfp_nkeys;
  	opt->flags = p->tcfp_flags;
  	opt->action = p->tcf_action;
  	opt->refcnt = p->tcf_refcnt - ref;
  	opt->bindcnt = p->tcf_bindcnt - bind;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385

71d0ed707   Amir Vadai   net/act_pedit: Su...
386
  	if (p->tcfp_keys_ex) {
417b068a6   Davide Caratti   net/sched: act_pe...
387
388
389
390
  		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...
391
392
393
394
395
396
397
  
  		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...
398
399
  
  	tcf_tm_dump(&t, &p->tcf_tm);
9854518ea   Nicolas Dichtel   sched: align nlat...
400
  	if (nla_put_64bit(skb, TCA_PEDIT_TM, sizeof(t), &t, TCA_PEDIT_PAD))
1b34ec43c   David S. Miller   pkt_sched: Stop u...
401
  		goto nla_put_failure;
48d8ee169   Jamal Hadi Salim   net sched actions...
402

541673c85   Patrick McHardy   [PKT_SCHED]: Fix ...
403
  	kfree(opt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
  	return skb->len;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
405
  nla_put_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
406
  	nlmsg_trim(skb, b);
541673c85   Patrick McHardy   [PKT_SCHED]: Fix ...
407
  	kfree(opt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
409
  	return -1;
  }
ddf97ccdd   WANG Cong   net_sched: add ne...
410
411
  static int tcf_pedit_walker(struct net *net, struct sk_buff *skb,
  			    struct netlink_callback *cb, int type,
a85a970af   WANG Cong   net_sched: move t...
412
  			    const struct tc_action_ops *ops)
ddf97ccdd   WANG Cong   net_sched: add ne...
413
414
  {
  	struct tc_action_net *tn = net_generic(net, pedit_net_id);
a85a970af   WANG Cong   net_sched: move t...
415
  	return tcf_generic_walker(tn, skb, cb, type, ops);
ddf97ccdd   WANG Cong   net_sched: add ne...
416
  }
a85a970af   WANG Cong   net_sched: move t...
417
  static int tcf_pedit_search(struct net *net, struct tc_action **a, u32 index)
ddf97ccdd   WANG Cong   net_sched: add ne...
418
419
  {
  	struct tc_action_net *tn = net_generic(net, pedit_net_id);
65a206c01   Chris Mi   net/sched: Change...
420
  	return tcf_idr_search(tn, a, index);
ddf97ccdd   WANG Cong   net_sched: add ne...
421
  }
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
422
  static struct tc_action_ops act_pedit_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
423
424
  	.kind		=	"pedit",
  	.type		=	TCA_ACT_PEDIT,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
426
427
428
  	.owner		=	THIS_MODULE,
  	.act		=	tcf_pedit,
  	.dump		=	tcf_pedit_dump,
  	.cleanup	=	tcf_pedit_cleanup,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
  	.init		=	tcf_pedit_init,
ddf97ccdd   WANG Cong   net_sched: add ne...
430
431
  	.walk		=	tcf_pedit_walker,
  	.lookup		=	tcf_pedit_search,
a85a970af   WANG Cong   net_sched: move t...
432
  	.size		=	sizeof(struct tcf_pedit),
ddf97ccdd   WANG Cong   net_sched: add ne...
433
434
435
436
437
  };
  
  static __net_init int pedit_init_net(struct net *net)
  {
  	struct tc_action_net *tn = net_generic(net, pedit_net_id);
c7e460ce5   Cong Wang   Revert "net_sched...
438
  	return tc_action_net_init(tn, &act_pedit_ops);
ddf97ccdd   WANG Cong   net_sched: add ne...
439
440
441
442
443
444
445
446
447
448
449
450
451
452
  }
  
  static void __net_exit pedit_exit_net(struct net *net)
  {
  	struct tc_action_net *tn = net_generic(net, pedit_net_id);
  
  	tc_action_net_exit(tn);
  }
  
  static struct pernet_operations pedit_net_ops = {
  	.init = pedit_init_net,
  	.exit = pedit_exit_net,
  	.id   = &pedit_net_id,
  	.size = sizeof(struct tc_action_net),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
454
455
456
457
  };
  
  MODULE_AUTHOR("Jamal Hadi Salim(2002-4)");
  MODULE_DESCRIPTION("Generic Packet Editor actions");
  MODULE_LICENSE("GPL");
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
458
  static int __init pedit_init_module(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
460
  	return tcf_register_action(&act_pedit_ops, &pedit_net_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
461
  }
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
462
  static void __exit pedit_cleanup_module(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
464
  	tcf_unregister_action(&act_pedit_ops, &pedit_net_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
466
467
468
  }
  
  module_init(pedit_init_module);
  module_exit(pedit_cleanup_module);