diff options
Diffstat (limited to 'net/xfrm/xfrm_state.c')
| -rw-r--r-- | net/xfrm/xfrm_state.c | 90 |
1 files changed, 58 insertions, 32 deletions
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index e25ff62ab2a6..62a5425cc6aa 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c | |||
| @@ -748,12 +748,51 @@ static void xfrm_hash_grow_check(struct net *net, int have_hash_collision) | |||
| 748 | schedule_work(&net->xfrm.state_hash_work); | 748 | schedule_work(&net->xfrm.state_hash_work); |
| 749 | } | 749 | } |
| 750 | 750 | ||
| 751 | static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x, | ||
| 752 | struct flowi *fl, unsigned short family, | ||
| 753 | xfrm_address_t *daddr, xfrm_address_t *saddr, | ||
| 754 | struct xfrm_state **best, int *acq_in_progress, | ||
| 755 | int *error) | ||
| 756 | { | ||
| 757 | /* Resolution logic: | ||
| 758 | * 1. There is a valid state with matching selector. Done. | ||
| 759 | * 2. Valid state with inappropriate selector. Skip. | ||
| 760 | * | ||
| 761 | * Entering area of "sysdeps". | ||
| 762 | * | ||
| 763 | * 3. If state is not valid, selector is temporary, it selects | ||
| 764 | * only session which triggered previous resolution. Key | ||
| 765 | * manager will do something to install a state with proper | ||
| 766 | * selector. | ||
| 767 | */ | ||
| 768 | if (x->km.state == XFRM_STATE_VALID) { | ||
| 769 | if ((x->sel.family && | ||
| 770 | !xfrm_selector_match(&x->sel, fl, x->sel.family)) || | ||
| 771 | !security_xfrm_state_pol_flow_match(x, pol, fl)) | ||
| 772 | return; | ||
| 773 | |||
| 774 | if (!*best || | ||
| 775 | (*best)->km.dying > x->km.dying || | ||
| 776 | ((*best)->km.dying == x->km.dying && | ||
| 777 | (*best)->curlft.add_time < x->curlft.add_time)) | ||
| 778 | *best = x; | ||
| 779 | } else if (x->km.state == XFRM_STATE_ACQ) { | ||
| 780 | *acq_in_progress = 1; | ||
| 781 | } else if (x->km.state == XFRM_STATE_ERROR || | ||
| 782 | x->km.state == XFRM_STATE_EXPIRED) { | ||
| 783 | if (xfrm_selector_match(&x->sel, fl, x->sel.family) && | ||
| 784 | security_xfrm_state_pol_flow_match(x, pol, fl)) | ||
| 785 | *error = -ESRCH; | ||
| 786 | } | ||
| 787 | } | ||
| 788 | |||
| 751 | struct xfrm_state * | 789 | struct xfrm_state * |
| 752 | xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, | 790 | xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, |
| 753 | struct flowi *fl, struct xfrm_tmpl *tmpl, | 791 | struct flowi *fl, struct xfrm_tmpl *tmpl, |
| 754 | struct xfrm_policy *pol, int *err, | 792 | struct xfrm_policy *pol, int *err, |
| 755 | unsigned short family) | 793 | unsigned short family) |
| 756 | { | 794 | { |
| 795 | static xfrm_address_t saddr_wildcard = { }; | ||
| 757 | struct net *net = xp_net(pol); | 796 | struct net *net = xp_net(pol); |
| 758 | unsigned int h; | 797 | unsigned int h; |
| 759 | struct hlist_node *entry; | 798 | struct hlist_node *entry; |
| @@ -773,40 +812,27 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, | |||
| 773 | xfrm_state_addr_check(x, daddr, saddr, family) && | 812 | xfrm_state_addr_check(x, daddr, saddr, family) && |
| 774 | tmpl->mode == x->props.mode && | 813 | tmpl->mode == x->props.mode && |
| 775 | tmpl->id.proto == x->id.proto && | 814 | tmpl->id.proto == x->id.proto && |
| 776 | (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) { | 815 | (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) |
| 777 | /* Resolution logic: | 816 | xfrm_state_look_at(pol, x, fl, family, daddr, saddr, |
| 778 | 1. There is a valid state with matching selector. | 817 | &best, &acquire_in_progress, &error); |
| 779 | Done. | 818 | } |
| 780 | 2. Valid state with inappropriate selector. Skip. | 819 | if (best) |
| 781 | 820 | goto found; | |
| 782 | Entering area of "sysdeps". | 821 | |
| 783 | 822 | h = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, family); | |
| 784 | 3. If state is not valid, selector is temporary, | 823 | hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) { |
| 785 | it selects only session which triggered | 824 | if (x->props.family == family && |
| 786 | previous resolution. Key manager will do | 825 | x->props.reqid == tmpl->reqid && |
| 787 | something to install a state with proper | 826 | !(x->props.flags & XFRM_STATE_WILDRECV) && |
| 788 | selector. | 827 | xfrm_state_addr_check(x, daddr, saddr, family) && |
| 789 | */ | 828 | tmpl->mode == x->props.mode && |
| 790 | if (x->km.state == XFRM_STATE_VALID) { | 829 | tmpl->id.proto == x->id.proto && |
| 791 | if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) || | 830 | (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) |
| 792 | !security_xfrm_state_pol_flow_match(x, pol, fl)) | 831 | xfrm_state_look_at(pol, x, fl, family, daddr, saddr, |
| 793 | continue; | 832 | &best, &acquire_in_progress, &error); |
| 794 | if (!best || | ||
| 795 | best->km.dying > x->km.dying || | ||
| 796 | (best->km.dying == x->km.dying && | ||
| 797 | best->curlft.add_time < x->curlft.add_time)) | ||
| 798 | best = x; | ||
| 799 | } else if (x->km.state == XFRM_STATE_ACQ) { | ||
| 800 | acquire_in_progress = 1; | ||
| 801 | } else if (x->km.state == XFRM_STATE_ERROR || | ||
| 802 | x->km.state == XFRM_STATE_EXPIRED) { | ||
| 803 | if (xfrm_selector_match(&x->sel, fl, x->sel.family) && | ||
| 804 | security_xfrm_state_pol_flow_match(x, pol, fl)) | ||
| 805 | error = -ESRCH; | ||
| 806 | } | ||
| 807 | } | ||
| 808 | } | 833 | } |
| 809 | 834 | ||
| 835 | found: | ||
| 810 | x = best; | 836 | x = best; |
| 811 | if (!x && !error && !acquire_in_progress) { | 837 | if (!x && !error && !acquire_in_progress) { |
| 812 | if (tmpl->id.spi && | 838 | if (tmpl->id.spi && |
