@@ -106,13 +106,12 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb,
106
106
107
107
static void tfilter_notify_chain (struct net * net , struct sk_buff * oskb ,
108
108
struct nlmsghdr * n ,
109
- struct tcf_proto __rcu * * chain , int event )
109
+ struct tcf_chain * chain , int event )
110
110
{
111
- struct tcf_proto __rcu * * it_chain ;
112
111
struct tcf_proto * tp ;
113
112
114
- for (it_chain = chain ; ( tp = rtnl_dereference (* it_chain )) != NULL ;
115
- it_chain = & tp -> next )
113
+ for (tp = rtnl_dereference (chain -> filter_chain ) ;
114
+ tp ; tp = rtnl_dereference ( tp -> next ) )
116
115
tfilter_notify (net , oskb , n , tp , 0 , event , false);
117
116
}
118
117
@@ -187,34 +186,57 @@ static void tcf_proto_destroy(struct tcf_proto *tp)
187
186
kfree_rcu (tp , rcu );
188
187
}
189
188
190
- static void tcf_chain_destroy (struct tcf_proto __rcu * * fl )
189
+ static struct tcf_chain * tcf_chain_create (void )
190
+ {
191
+ return kzalloc (sizeof (struct tcf_chain ), GFP_KERNEL );
192
+ }
193
+
194
+ static void tcf_chain_destroy (struct tcf_chain * chain )
191
195
{
192
196
struct tcf_proto * tp ;
193
197
194
- while ((tp = rtnl_dereference (* fl )) != NULL ) {
195
- RCU_INIT_POINTER (* fl , tp -> next );
198
+ while ((tp = rtnl_dereference (chain -> filter_chain )) != NULL ) {
199
+ RCU_INIT_POINTER (chain -> filter_chain , tp -> next );
196
200
tcf_proto_destroy (tp );
197
201
}
202
+ kfree (chain );
203
+ }
204
+
205
+ static void
206
+ tcf_chain_filter_chain_ptr_set (struct tcf_chain * chain ,
207
+ struct tcf_proto __rcu * * p_filter_chain )
208
+ {
209
+ chain -> p_filter_chain = p_filter_chain ;
198
210
}
199
211
200
212
int tcf_block_get (struct tcf_block * * p_block ,
201
213
struct tcf_proto __rcu * * p_filter_chain )
202
214
{
203
215
struct tcf_block * block = kzalloc (sizeof (* block ), GFP_KERNEL );
216
+ int err ;
204
217
205
218
if (!block )
206
219
return - ENOMEM ;
207
- block -> p_filter_chain = p_filter_chain ;
220
+ block -> chain = tcf_chain_create ();
221
+ if (!block -> chain ) {
222
+ err = - ENOMEM ;
223
+ goto err_chain_create ;
224
+ }
225
+ tcf_chain_filter_chain_ptr_set (block -> chain , p_filter_chain );
208
226
* p_block = block ;
209
227
return 0 ;
228
+
229
+ err_chain_create :
230
+ kfree (block );
231
+ return err ;
210
232
}
211
233
EXPORT_SYMBOL (tcf_block_get );
212
234
213
235
void tcf_block_put (struct tcf_block * block )
214
236
{
215
237
if (!block )
216
238
return ;
217
- tcf_chain_destroy (block -> p_filter_chain );
239
+ tcf_chain_destroy (block -> chain );
218
240
kfree (block );
219
241
}
220
242
EXPORT_SYMBOL (tcf_block_put );
@@ -267,6 +289,65 @@ int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
267
289
}
268
290
EXPORT_SYMBOL (tcf_classify );
269
291
292
+ struct tcf_chain_info {
293
+ struct tcf_proto __rcu * * pprev ;
294
+ struct tcf_proto __rcu * next ;
295
+ };
296
+
297
+ static struct tcf_proto * tcf_chain_tp_prev (struct tcf_chain_info * chain_info )
298
+ {
299
+ return rtnl_dereference (* chain_info -> pprev );
300
+ }
301
+
302
+ static void tcf_chain_tp_insert (struct tcf_chain * chain ,
303
+ struct tcf_chain_info * chain_info ,
304
+ struct tcf_proto * tp )
305
+ {
306
+ if (chain -> p_filter_chain &&
307
+ * chain_info -> pprev == chain -> filter_chain )
308
+ * chain -> p_filter_chain = tp ;
309
+ RCU_INIT_POINTER (tp -> next , tcf_chain_tp_prev (chain_info ));
310
+ rcu_assign_pointer (* chain_info -> pprev , tp );
311
+ }
312
+
313
+ static void tcf_chain_tp_remove (struct tcf_chain * chain ,
314
+ struct tcf_chain_info * chain_info ,
315
+ struct tcf_proto * tp )
316
+ {
317
+ struct tcf_proto * next = rtnl_dereference (chain_info -> next );
318
+
319
+ if (chain -> p_filter_chain && tp == chain -> filter_chain )
320
+ * chain -> p_filter_chain = next ;
321
+ RCU_INIT_POINTER (* chain_info -> pprev , next );
322
+ }
323
+
324
+ static struct tcf_proto * tcf_chain_tp_find (struct tcf_chain * chain ,
325
+ struct tcf_chain_info * chain_info ,
326
+ u32 protocol , u32 prio ,
327
+ bool prio_allocate )
328
+ {
329
+ struct tcf_proto * * pprev ;
330
+ struct tcf_proto * tp ;
331
+
332
+ /* Check the chain for existence of proto-tcf with this priority */
333
+ for (pprev = & chain -> filter_chain ;
334
+ (tp = rtnl_dereference (* pprev )); pprev = & tp -> next ) {
335
+ if (tp -> prio >= prio ) {
336
+ if (tp -> prio == prio ) {
337
+ if (prio_allocate ||
338
+ (tp -> protocol != protocol && protocol ))
339
+ return ERR_PTR (- EINVAL );
340
+ } else {
341
+ tp = NULL ;
342
+ }
343
+ break ;
344
+ }
345
+ }
346
+ chain_info -> pprev = pprev ;
347
+ chain_info -> next = tp ? tp -> next : NULL ;
348
+ return tp ;
349
+ }
350
+
270
351
/* Add/change/delete/get a filter node */
271
352
272
353
static int tc_ctl_tfilter (struct sk_buff * skb , struct nlmsghdr * n ,
@@ -281,10 +362,9 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
281
362
u32 parent ;
282
363
struct net_device * dev ;
283
364
struct Qdisc * q ;
284
- struct tcf_proto __rcu * * back ;
285
- struct tcf_proto __rcu * * chain ;
365
+ struct tcf_chain_info chain_info ;
366
+ struct tcf_chain * chain ;
286
367
struct tcf_block * block ;
287
- struct tcf_proto * next ;
288
368
struct tcf_proto * tp ;
289
369
const struct Qdisc_class_ops * cops ;
290
370
unsigned long cl ;
@@ -369,7 +449,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
369
449
err = - EINVAL ;
370
450
goto errout ;
371
451
}
372
- chain = block -> p_filter_chain ;
452
+ chain = block -> chain ;
373
453
374
454
if (n -> nlmsg_type == RTM_DELTFILTER && prio == 0 ) {
375
455
tfilter_notify_chain (net , skb , n , chain , RTM_DELTFILTER );
@@ -378,22 +458,11 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
378
458
goto errout ;
379
459
}
380
460
381
- /* Check the chain for existence of proto-tcf with this priority */
382
- for (back = chain ;
383
- (tp = rtnl_dereference (* back )) != NULL ;
384
- back = & tp -> next ) {
385
- if (tp -> prio >= prio ) {
386
- if (tp -> prio == prio ) {
387
- if (prio_allocate ||
388
- (tp -> protocol != protocol && protocol )) {
389
- err = - EINVAL ;
390
- goto errout ;
391
- }
392
- } else {
393
- tp = NULL ;
394
- }
395
- break ;
396
- }
461
+ tp = tcf_chain_tp_find (chain , & chain_info , protocol ,
462
+ prio , prio_allocate );
463
+ if (IS_ERR (tp )) {
464
+ err = PTR_ERR (tp );
465
+ goto errout ;
397
466
}
398
467
399
468
if (tp == NULL ) {
@@ -411,7 +480,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
411
480
}
412
481
413
482
if (prio_allocate )
414
- prio = tcf_auto_prio (rtnl_dereference ( * back ));
483
+ prio = tcf_auto_prio (tcf_chain_tp_prev ( & chain_info ));
415
484
416
485
tp = tcf_proto_create (nla_data (tca [TCA_KIND ]),
417
486
protocol , prio , parent , q , block );
@@ -429,8 +498,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
429
498
430
499
if (fh == 0 ) {
431
500
if (n -> nlmsg_type == RTM_DELTFILTER && t -> tcm_handle == 0 ) {
432
- next = rtnl_dereference (tp -> next );
433
- RCU_INIT_POINTER (* back , next );
501
+ tcf_chain_tp_remove (chain , & chain_info , tp );
434
502
tfilter_notify (net , skb , n , tp , fh ,
435
503
RTM_DELTFILTER , false);
436
504
tcf_proto_destroy (tp );
@@ -459,11 +527,10 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
459
527
err = tp -> ops -> delete (tp , fh , & last );
460
528
if (err )
461
529
goto errout ;
462
- next = rtnl_dereference (tp -> next );
463
530
tfilter_notify (net , skb , n , tp , t -> tcm_handle ,
464
531
RTM_DELTFILTER , false);
465
532
if (last ) {
466
- RCU_INIT_POINTER ( * back , next );
533
+ tcf_chain_tp_remove ( chain , & chain_info , tp );
467
534
tcf_proto_destroy (tp );
468
535
}
469
536
goto errout ;
@@ -480,10 +547,8 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
480
547
err = tp -> ops -> change (net , skb , tp , cl , t -> tcm_handle , tca , & fh ,
481
548
n -> nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE );
482
549
if (err == 0 ) {
483
- if (tp_created ) {
484
- RCU_INIT_POINTER (tp -> next , rtnl_dereference (* back ));
485
- rcu_assign_pointer (* back , tp );
486
- }
550
+ if (tp_created )
551
+ tcf_chain_tp_insert (chain , & chain_info , tp );
487
552
tfilter_notify (net , skb , n , tp , fh , RTM_NEWTFILTER , false);
488
553
} else {
489
554
if (tp_created )
@@ -584,7 +649,8 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
584
649
struct net_device * dev ;
585
650
struct Qdisc * q ;
586
651
struct tcf_block * block ;
587
- struct tcf_proto * tp , __rcu * * chain ;
652
+ struct tcf_proto * tp ;
653
+ struct tcf_chain * chain ;
588
654
struct tcmsg * tcm = nlmsg_data (cb -> nlh );
589
655
unsigned long cl = 0 ;
590
656
const struct Qdisc_class_ops * cops ;
@@ -615,11 +681,11 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
615
681
block = cops -> tcf_block (q , cl );
616
682
if (!block )
617
683
goto errout ;
618
- chain = block -> p_filter_chain ;
684
+ chain = block -> chain ;
619
685
620
686
s_t = cb -> args [0 ];
621
687
622
- for (tp = rtnl_dereference (* chain ), t = 0 ;
688
+ for (tp = rtnl_dereference (chain -> filter_chain ), t = 0 ;
623
689
tp ; tp = rtnl_dereference (tp -> next ), t ++ ) {
624
690
if (t < s_t )
625
691
continue ;
0 commit comments