22
22
#include <net/pkt_sched.h>
23
23
#include <linux/tc_act/tc_pedit.h>
24
24
#include <net/tc_act/tc_pedit.h>
25
+ #include <uapi/linux/tc_act/tc_pedit.h>
25
26
26
27
#define PEDIT_TAB_MASK 15
27
28
@@ -30,18 +31,112 @@ static struct tc_action_ops act_pedit_ops;
30
31
31
32
static const struct nla_policy pedit_policy [TCA_PEDIT_MAX + 1 ] = {
32
33
[TCA_PEDIT_PARMS ] = { .len = sizeof (struct tc_pedit ) },
34
+ [TCA_PEDIT_KEYS_EX ] = { .type = NLA_NESTED },
33
35
};
34
36
37
+ static const struct nla_policy pedit_key_ex_policy [TCA_PEDIT_KEY_EX_MAX + 1 ] = {
38
+ [TCA_PEDIT_KEY_EX_HTYPE ] = { .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 (tb , TCA_PEDIT_KEY_EX_MAX , ka ,
74
+ pedit_key_ex_policy );
75
+ if (err )
76
+ goto err_out ;
77
+
78
+ if (!tb [TCA_PEDIT_KEY_EX_HTYPE ]) {
79
+ err = - EINVAL ;
80
+ goto err_out ;
81
+ }
82
+
83
+ k -> htype = nla_get_u16 (tb [TCA_PEDIT_KEY_EX_HTYPE ]);
84
+
85
+ if (k -> htype > TCA_PEDIT_HDR_TYPE_MAX ) {
86
+ err = - EINVAL ;
87
+ goto err_out ;
88
+ }
89
+
90
+ k ++ ;
91
+ }
92
+
93
+ if (n )
94
+ goto err_out ;
95
+
96
+ return keys_ex ;
97
+
98
+ err_out :
99
+ kfree (keys_ex );
100
+ return ERR_PTR (err );
101
+ }
102
+
103
+ static int tcf_pedit_key_ex_dump (struct sk_buff * skb ,
104
+ struct tcf_pedit_key_ex * keys_ex , int n )
105
+ {
106
+ struct nlattr * keys_start = nla_nest_start (skb , TCA_PEDIT_KEYS_EX );
107
+
108
+ for (; n > 0 ; n -- ) {
109
+ struct nlattr * key_start ;
110
+
111
+ key_start = nla_nest_start (skb , TCA_PEDIT_KEY_EX );
112
+
113
+ if (nla_put_u16 (skb , TCA_PEDIT_KEY_EX_HTYPE , keys_ex -> htype )) {
114
+ nlmsg_trim (skb , keys_start );
115
+ return - EINVAL ;
116
+ }
117
+
118
+ nla_nest_end (skb , key_start );
119
+
120
+ keys_ex ++ ;
121
+ }
122
+
123
+ nla_nest_end (skb , keys_start );
124
+
125
+ return 0 ;
126
+ }
127
+
35
128
static int tcf_pedit_init (struct net * net , struct nlattr * nla ,
36
129
struct nlattr * est , struct tc_action * * a ,
37
130
int ovr , int bind )
38
131
{
39
132
struct tc_action_net * tn = net_generic (net , pedit_net_id );
40
133
struct nlattr * tb [TCA_PEDIT_MAX + 1 ];
134
+ struct nlattr * pattr ;
41
135
struct tc_pedit * parm ;
42
136
int ret = 0 , err ;
43
137
struct tcf_pedit * p ;
44
138
struct tc_pedit_key * keys = NULL ;
139
+ struct tcf_pedit_key_ex * keys_ex ;
45
140
int ksize ;
46
141
47
142
if (nla == NULL )
@@ -51,13 +146,21 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
51
146
if (err < 0 )
52
147
return err ;
53
148
54
- if (tb [TCA_PEDIT_PARMS ] == NULL )
149
+ pattr = tb [TCA_PEDIT_PARMS ];
150
+ if (!pattr )
151
+ pattr = tb [TCA_PEDIT_PARMS_EX ];
152
+ if (!pattr )
55
153
return - EINVAL ;
56
- parm = nla_data (tb [TCA_PEDIT_PARMS ]);
154
+
155
+ parm = nla_data (pattr );
57
156
ksize = parm -> nkeys * sizeof (struct tc_pedit_key );
58
- if (nla_len (tb [ TCA_PEDIT_PARMS ] ) < sizeof (* parm ) + ksize )
157
+ if (nla_len (pattr ) < sizeof (* parm ) + ksize )
59
158
return - EINVAL ;
60
159
160
+ keys_ex = tcf_pedit_keys_ex_parse (tb [TCA_PEDIT_KEYS_EX ], parm -> nkeys );
161
+ if (IS_ERR (keys_ex ))
162
+ return PTR_ERR (keys_ex );
163
+
61
164
if (!tcf_hash_check (tn , parm -> index , a , bind )) {
62
165
if (!parm -> nkeys )
63
166
return - EINVAL ;
@@ -69,6 +172,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
69
172
keys = kmalloc (ksize , GFP_KERNEL );
70
173
if (keys == NULL ) {
71
174
tcf_hash_cleanup (* a , est );
175
+ kfree (keys_ex );
72
176
return - ENOMEM ;
73
177
}
74
178
ret = ACT_P_CREATED ;
@@ -81,8 +185,10 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
81
185
p = to_pedit (* a );
82
186
if (p -> tcfp_nkeys && p -> tcfp_nkeys != parm -> nkeys ) {
83
187
keys = kmalloc (ksize , GFP_KERNEL );
84
- if (keys == NULL )
188
+ if (!keys ) {
189
+ kfree (keys_ex );
85
190
return - ENOMEM ;
191
+ }
86
192
}
87
193
}
88
194
@@ -95,6 +201,10 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
95
201
p -> tcfp_nkeys = parm -> nkeys ;
96
202
}
97
203
memcpy (p -> tcfp_keys , parm -> keys , ksize );
204
+
205
+ kfree (p -> tcfp_keys_ex );
206
+ p -> tcfp_keys_ex = keys_ex ;
207
+
98
208
spin_unlock_bh (& p -> tcf_lock );
99
209
if (ret == ACT_P_CREATED )
100
210
tcf_hash_insert (tn , * a );
@@ -106,6 +216,7 @@ static void tcf_pedit_cleanup(struct tc_action *a, int bind)
106
216
struct tcf_pedit * p = to_pedit (a );
107
217
struct tc_pedit_key * keys = p -> tcfp_keys ;
108
218
kfree (keys );
219
+ kfree (p -> tcfp_keys_ex );
109
220
}
110
221
111
222
static bool offset_valid (struct sk_buff * skb , int offset )
@@ -119,38 +230,84 @@ static bool offset_valid(struct sk_buff *skb, int offset)
119
230
return true;
120
231
}
121
232
233
+ static int pedit_skb_hdr_offset (struct sk_buff * skb ,
234
+ enum pedit_header_type htype , int * hoffset )
235
+ {
236
+ int ret = - EINVAL ;
237
+
238
+ switch (htype ) {
239
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH :
240
+ if (skb_mac_header_was_set (skb )) {
241
+ * hoffset = skb_mac_offset (skb );
242
+ ret = 0 ;
243
+ }
244
+ break ;
245
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK :
246
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4 :
247
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6 :
248
+ * hoffset = skb_network_offset (skb );
249
+ ret = 0 ;
250
+ break ;
251
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP :
252
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP :
253
+ if (skb_transport_header_was_set (skb )) {
254
+ * hoffset = skb_transport_offset (skb );
255
+ ret = 0 ;
256
+ }
257
+ break ;
258
+ default :
259
+ ret = - EINVAL ;
260
+ break ;
261
+ };
262
+
263
+ return ret ;
264
+ }
265
+
122
266
static int tcf_pedit (struct sk_buff * skb , const struct tc_action * a ,
123
267
struct tcf_result * res )
124
268
{
125
269
struct tcf_pedit * p = to_pedit (a );
126
270
int i ;
127
- unsigned int off ;
128
271
129
272
if (skb_unclone (skb , GFP_ATOMIC ))
130
273
return p -> tcf_action ;
131
274
132
- off = skb_network_offset (skb );
133
-
134
275
spin_lock (& p -> tcf_lock );
135
276
136
277
tcf_lastuse_update (& p -> tcf_tm );
137
278
138
279
if (p -> tcfp_nkeys > 0 ) {
139
280
struct tc_pedit_key * tkey = p -> tcfp_keys ;
281
+ struct tcf_pedit_key_ex * tkey_ex = p -> tcfp_keys_ex ;
282
+ enum pedit_header_type htype = TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK ;
140
283
141
284
for (i = p -> tcfp_nkeys ; i > 0 ; i -- , tkey ++ ) {
142
285
u32 * ptr , _data ;
143
286
int offset = tkey -> off ;
287
+ int hoffset ;
288
+ int rc ;
289
+
290
+ if (tkey_ex ) {
291
+ htype = tkey_ex -> htype ;
292
+ tkey_ex ++ ;
293
+ }
294
+
295
+ rc = pedit_skb_hdr_offset (skb , htype , & hoffset );
296
+ if (rc ) {
297
+ pr_info ("tc filter pedit bad header type specified (0x%x)\n" ,
298
+ htype );
299
+ goto bad ;
300
+ }
144
301
145
302
if (tkey -> offmask ) {
146
303
char * d , _d ;
147
304
148
- if (!offset_valid (skb , off + tkey -> at )) {
305
+ if (!offset_valid (skb , hoffset + tkey -> at )) {
149
306
pr_info ("tc filter pedit 'at' offset %d out of bounds\n" ,
150
- off + tkey -> at );
307
+ hoffset + tkey -> at );
151
308
goto bad ;
152
309
}
153
- d = skb_header_pointer (skb , off + tkey -> at , 1 ,
310
+ d = skb_header_pointer (skb , hoffset + tkey -> at , 1 ,
154
311
& _d );
155
312
if (!d )
156
313
goto bad ;
@@ -163,19 +320,19 @@ static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a,
163
320
goto bad ;
164
321
}
165
322
166
- if (!offset_valid (skb , off + offset )) {
323
+ if (!offset_valid (skb , hoffset + offset )) {
167
324
pr_info ("tc filter pedit offset %d out of bounds\n" ,
168
- offset );
325
+ hoffset + offset );
169
326
goto bad ;
170
327
}
171
328
172
- ptr = skb_header_pointer (skb , off + offset , 4 , & _data );
329
+ ptr = skb_header_pointer (skb , hoffset + offset , 4 , & _data );
173
330
if (!ptr )
174
331
goto bad ;
175
332
/* just do it, baby */
176
333
* ptr = ((* ptr & tkey -> mask ) ^ tkey -> val );
177
334
if (ptr == & _data )
178
- skb_store_bits (skb , off + offset , ptr , 4 );
335
+ skb_store_bits (skb , hoffset + offset , ptr , 4 );
179
336
}
180
337
181
338
goto done ;
@@ -215,8 +372,15 @@ static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a,
215
372
opt -> refcnt = p -> tcf_refcnt - ref ;
216
373
opt -> bindcnt = p -> tcf_bindcnt - bind ;
217
374
218
- if (nla_put (skb , TCA_PEDIT_PARMS , s , opt ))
219
- goto nla_put_failure ;
375
+ if (p -> tcfp_keys_ex ) {
376
+ tcf_pedit_key_ex_dump (skb , p -> tcfp_keys_ex , p -> tcfp_nkeys );
377
+
378
+ if (nla_put (skb , TCA_PEDIT_PARMS_EX , s , opt ))
379
+ goto nla_put_failure ;
380
+ } else {
381
+ if (nla_put (skb , TCA_PEDIT_PARMS , s , opt ))
382
+ goto nla_put_failure ;
383
+ }
220
384
221
385
tcf_tm_dump (& t , & p -> tcf_tm );
222
386
if (nla_put_64bit (skb , TCA_PEDIT_TM , sizeof (t ), & t , TCA_PEDIT_PAD ))
0 commit comments