diff options
Diffstat (limited to 'net/xfrm/xfrm_policy.c')
-rw-r--r-- | net/xfrm/xfrm_policy.c | 144 |
1 files changed, 135 insertions, 9 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index fdde51f4271a..4c4e457e7888 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c | |||
@@ -349,12 +349,39 @@ static inline unsigned int idx_hash(struct net *net, u32 index) | |||
349 | return __idx_hash(index, net->xfrm.policy_idx_hmask); | 349 | return __idx_hash(index, net->xfrm.policy_idx_hmask); |
350 | } | 350 | } |
351 | 351 | ||
352 | /* calculate policy hash thresholds */ | ||
353 | static void __get_hash_thresh(struct net *net, | ||
354 | unsigned short family, int dir, | ||
355 | u8 *dbits, u8 *sbits) | ||
356 | { | ||
357 | switch (family) { | ||
358 | case AF_INET: | ||
359 | *dbits = net->xfrm.policy_bydst[dir].dbits4; | ||
360 | *sbits = net->xfrm.policy_bydst[dir].sbits4; | ||
361 | break; | ||
362 | |||
363 | case AF_INET6: | ||
364 | *dbits = net->xfrm.policy_bydst[dir].dbits6; | ||
365 | *sbits = net->xfrm.policy_bydst[dir].sbits6; | ||
366 | break; | ||
367 | |||
368 | default: | ||
369 | *dbits = 0; | ||
370 | *sbits = 0; | ||
371 | } | ||
372 | } | ||
373 | |||
352 | static struct hlist_head *policy_hash_bysel(struct net *net, | 374 | static struct hlist_head *policy_hash_bysel(struct net *net, |
353 | const struct xfrm_selector *sel, | 375 | const struct xfrm_selector *sel, |
354 | unsigned short family, int dir) | 376 | unsigned short family, int dir) |
355 | { | 377 | { |
356 | unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; | 378 | unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; |
357 | unsigned int hash = __sel_hash(sel, family, hmask); | 379 | unsigned int hash; |
380 | u8 dbits; | ||
381 | u8 sbits; | ||
382 | |||
383 | __get_hash_thresh(net, family, dir, &dbits, &sbits); | ||
384 | hash = __sel_hash(sel, family, hmask, dbits, sbits); | ||
358 | 385 | ||
359 | return (hash == hmask + 1 ? | 386 | return (hash == hmask + 1 ? |
360 | &net->xfrm.policy_inexact[dir] : | 387 | &net->xfrm.policy_inexact[dir] : |
@@ -367,25 +394,35 @@ static struct hlist_head *policy_hash_direct(struct net *net, | |||
367 | unsigned short family, int dir) | 394 | unsigned short family, int dir) |
368 | { | 395 | { |
369 | unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; | 396 | unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; |
370 | unsigned int hash = __addr_hash(daddr, saddr, family, hmask); | 397 | unsigned int hash; |
398 | u8 dbits; | ||
399 | u8 sbits; | ||
400 | |||
401 | __get_hash_thresh(net, family, dir, &dbits, &sbits); | ||
402 | hash = __addr_hash(daddr, saddr, family, hmask, dbits, sbits); | ||
371 | 403 | ||
372 | return net->xfrm.policy_bydst[dir].table + hash; | 404 | return net->xfrm.policy_bydst[dir].table + hash; |
373 | } | 405 | } |
374 | 406 | ||
375 | static void xfrm_dst_hash_transfer(struct hlist_head *list, | 407 | static void xfrm_dst_hash_transfer(struct net *net, |
408 | struct hlist_head *list, | ||
376 | struct hlist_head *ndsttable, | 409 | struct hlist_head *ndsttable, |
377 | unsigned int nhashmask) | 410 | unsigned int nhashmask, |
411 | int dir) | ||
378 | { | 412 | { |
379 | struct hlist_node *tmp, *entry0 = NULL; | 413 | struct hlist_node *tmp, *entry0 = NULL; |
380 | struct xfrm_policy *pol; | 414 | struct xfrm_policy *pol; |
381 | unsigned int h0 = 0; | 415 | unsigned int h0 = 0; |
416 | u8 dbits; | ||
417 | u8 sbits; | ||
382 | 418 | ||
383 | redo: | 419 | redo: |
384 | hlist_for_each_entry_safe(pol, tmp, list, bydst) { | 420 | hlist_for_each_entry_safe(pol, tmp, list, bydst) { |
385 | unsigned int h; | 421 | unsigned int h; |
386 | 422 | ||
423 | __get_hash_thresh(net, pol->family, dir, &dbits, &sbits); | ||
387 | h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr, | 424 | h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr, |
388 | pol->family, nhashmask); | 425 | pol->family, nhashmask, dbits, sbits); |
389 | if (!entry0) { | 426 | if (!entry0) { |
390 | hlist_del(&pol->bydst); | 427 | hlist_del(&pol->bydst); |
391 | hlist_add_head(&pol->bydst, ndsttable+h); | 428 | hlist_add_head(&pol->bydst, ndsttable+h); |
@@ -439,7 +476,7 @@ static void xfrm_bydst_resize(struct net *net, int dir) | |||
439 | write_lock_bh(&net->xfrm.xfrm_policy_lock); | 476 | write_lock_bh(&net->xfrm.xfrm_policy_lock); |
440 | 477 | ||
441 | for (i = hmask; i >= 0; i--) | 478 | for (i = hmask; i >= 0; i--) |
442 | xfrm_dst_hash_transfer(odst + i, ndst, nhashmask); | 479 | xfrm_dst_hash_transfer(net, odst + i, ndst, nhashmask, dir); |
443 | 480 | ||
444 | net->xfrm.policy_bydst[dir].table = ndst; | 481 | net->xfrm.policy_bydst[dir].table = ndst; |
445 | net->xfrm.policy_bydst[dir].hmask = nhashmask; | 482 | net->xfrm.policy_bydst[dir].hmask = nhashmask; |
@@ -534,6 +571,86 @@ static void xfrm_hash_resize(struct work_struct *work) | |||
534 | mutex_unlock(&hash_resize_mutex); | 571 | mutex_unlock(&hash_resize_mutex); |
535 | } | 572 | } |
536 | 573 | ||
574 | static void xfrm_hash_rebuild(struct work_struct *work) | ||
575 | { | ||
576 | struct net *net = container_of(work, struct net, | ||
577 | xfrm.policy_hthresh.work); | ||
578 | unsigned int hmask; | ||
579 | struct xfrm_policy *pol; | ||
580 | struct xfrm_policy *policy; | ||
581 | struct hlist_head *chain; | ||
582 | struct hlist_head *odst; | ||
583 | struct hlist_node *newpos; | ||
584 | int i; | ||
585 | int dir; | ||
586 | unsigned seq; | ||
587 | u8 lbits4, rbits4, lbits6, rbits6; | ||
588 | |||
589 | mutex_lock(&hash_resize_mutex); | ||
590 | |||
591 | /* read selector prefixlen thresholds */ | ||
592 | do { | ||
593 | seq = read_seqbegin(&net->xfrm.policy_hthresh.lock); | ||
594 | |||
595 | lbits4 = net->xfrm.policy_hthresh.lbits4; | ||
596 | rbits4 = net->xfrm.policy_hthresh.rbits4; | ||
597 | lbits6 = net->xfrm.policy_hthresh.lbits6; | ||
598 | rbits6 = net->xfrm.policy_hthresh.rbits6; | ||
599 | } while (read_seqretry(&net->xfrm.policy_hthresh.lock, seq)); | ||
600 | |||
601 | write_lock_bh(&net->xfrm.xfrm_policy_lock); | ||
602 | |||
603 | /* reset the bydst and inexact table in all directions */ | ||
604 | for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) { | ||
605 | INIT_HLIST_HEAD(&net->xfrm.policy_inexact[dir]); | ||
606 | hmask = net->xfrm.policy_bydst[dir].hmask; | ||
607 | odst = net->xfrm.policy_bydst[dir].table; | ||
608 | for (i = hmask; i >= 0; i--) | ||
609 | INIT_HLIST_HEAD(odst + i); | ||
610 | if ((dir & XFRM_POLICY_MASK) == XFRM_POLICY_OUT) { | ||
611 | /* dir out => dst = remote, src = local */ | ||
612 | net->xfrm.policy_bydst[dir].dbits4 = rbits4; | ||
613 | net->xfrm.policy_bydst[dir].sbits4 = lbits4; | ||
614 | net->xfrm.policy_bydst[dir].dbits6 = rbits6; | ||
615 | net->xfrm.policy_bydst[dir].sbits6 = lbits6; | ||
616 | } else { | ||
617 | /* dir in/fwd => dst = local, src = remote */ | ||
618 | net->xfrm.policy_bydst[dir].dbits4 = lbits4; | ||
619 | net->xfrm.policy_bydst[dir].sbits4 = rbits4; | ||
620 | net->xfrm.policy_bydst[dir].dbits6 = lbits6; | ||
621 | net->xfrm.policy_bydst[dir].sbits6 = rbits6; | ||
622 | } | ||
623 | } | ||
624 | |||
625 | /* re-insert all policies by order of creation */ | ||
626 | list_for_each_entry_reverse(policy, &net->xfrm.policy_all, walk.all) { | ||
627 | newpos = NULL; | ||
628 | chain = policy_hash_bysel(net, &policy->selector, | ||
629 | policy->family, | ||
630 | xfrm_policy_id2dir(policy->index)); | ||
631 | hlist_for_each_entry(pol, chain, bydst) { | ||
632 | if (policy->priority >= pol->priority) | ||
633 | newpos = &pol->bydst; | ||
634 | else | ||
635 | break; | ||
636 | } | ||
637 | if (newpos) | ||
638 | hlist_add_behind(&policy->bydst, newpos); | ||
639 | else | ||
640 | hlist_add_head(&policy->bydst, chain); | ||
641 | } | ||
642 | |||
643 | write_unlock_bh(&net->xfrm.xfrm_policy_lock); | ||
644 | |||
645 | mutex_unlock(&hash_resize_mutex); | ||
646 | } | ||
647 | |||
648 | void xfrm_policy_hash_rebuild(struct net *net) | ||
649 | { | ||
650 | schedule_work(&net->xfrm.policy_hthresh.work); | ||
651 | } | ||
652 | EXPORT_SYMBOL(xfrm_policy_hash_rebuild); | ||
653 | |||
537 | /* Generate new index... KAME seems to generate them ordered by cost | 654 | /* Generate new index... KAME seems to generate them ordered by cost |
538 | * of an absolute inpredictability of ordering of rules. This will not pass. */ | 655 | * of an absolute inpredictability of ordering of rules. This will not pass. */ |
539 | static u32 xfrm_gen_index(struct net *net, int dir, u32 index) | 656 | static u32 xfrm_gen_index(struct net *net, int dir, u32 index) |
@@ -1844,10 +1961,8 @@ static int xdst_queue_output(struct sock *sk, struct sk_buff *skb) | |||
1844 | struct xfrm_dst *xdst = (struct xfrm_dst *) dst; | 1961 | struct xfrm_dst *xdst = (struct xfrm_dst *) dst; |
1845 | struct xfrm_policy *pol = xdst->pols[0]; | 1962 | struct xfrm_policy *pol = xdst->pols[0]; |
1846 | struct xfrm_policy_queue *pq = &pol->polq; | 1963 | struct xfrm_policy_queue *pq = &pol->polq; |
1847 | const struct sk_buff *fclone = skb + 1; | ||
1848 | 1964 | ||
1849 | if (unlikely(skb->fclone == SKB_FCLONE_ORIG && | 1965 | if (unlikely(skb_fclone_busy(skb))) { |
1850 | fclone->fclone == SKB_FCLONE_CLONE)) { | ||
1851 | kfree_skb(skb); | 1966 | kfree_skb(skb); |
1852 | return 0; | 1967 | return 0; |
1853 | } | 1968 | } |
@@ -2862,10 +2977,21 @@ static int __net_init xfrm_policy_init(struct net *net) | |||
2862 | if (!htab->table) | 2977 | if (!htab->table) |
2863 | goto out_bydst; | 2978 | goto out_bydst; |
2864 | htab->hmask = hmask; | 2979 | htab->hmask = hmask; |
2980 | htab->dbits4 = 32; | ||
2981 | htab->sbits4 = 32; | ||
2982 | htab->dbits6 = 128; | ||
2983 | htab->sbits6 = 128; | ||
2865 | } | 2984 | } |
2985 | net->xfrm.policy_hthresh.lbits4 = 32; | ||
2986 | net->xfrm.policy_hthresh.rbits4 = 32; | ||
2987 | net->xfrm.policy_hthresh.lbits6 = 128; | ||
2988 | net->xfrm.policy_hthresh.rbits6 = 128; | ||
2989 | |||
2990 | seqlock_init(&net->xfrm.policy_hthresh.lock); | ||
2866 | 2991 | ||
2867 | INIT_LIST_HEAD(&net->xfrm.policy_all); | 2992 | INIT_LIST_HEAD(&net->xfrm.policy_all); |
2868 | INIT_WORK(&net->xfrm.policy_hash_work, xfrm_hash_resize); | 2993 | INIT_WORK(&net->xfrm.policy_hash_work, xfrm_hash_resize); |
2994 | INIT_WORK(&net->xfrm.policy_hthresh.work, xfrm_hash_rebuild); | ||
2869 | if (net_eq(net, &init_net)) | 2995 | if (net_eq(net, &init_net)) |
2870 | register_netdevice_notifier(&xfrm_dev_notifier); | 2996 | register_netdevice_notifier(&xfrm_dev_notifier); |
2871 | return 0; | 2997 | return 0; |