diff options
| author | Thomas Egerer <thomas.egerer@secunet.com> | 2010-09-20 14:11:38 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2010-09-20 14:11:38 -0400 |
| commit | 8444cf712c5f71845cba9dc30d8f530ff0d5ff83 (patch) | |
| tree | 3283983551829eb3f985a92d49ec65ac8e487268 | |
| parent | 842c74bffcdb1d305ccd9e61e417cceae86b9963 (diff) | |
xfrm: Allow different selector family in temporary state
The family parameter xfrm_state_find is used to find a state matching a
certain policy. This value is set to the template's family
(encap_family) right before xfrm_state_find is called.
The family parameter is however also used to construct a temporary state
in xfrm_state_find itself which is wrong for inter-family scenarios
because it produces a selector for the wrong family. Since this selector
is included in the xfrm_user_acquire structure, user space programs
misinterpret IPv6 addresses as IPv4 and vice versa.
This patch splits up the original init_tempsel function into a part that
initializes the selector respectively the props and id of the temporary
state, to allow for differing ip address families whithin the state.
Signed-off-by: Thomas Egerer <thomas.egerer@secunet.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | include/net/xfrm.h | 4 | ||||
| -rw-r--r-- | net/ipv4/xfrm4_state.c | 33 | ||||
| -rw-r--r-- | net/ipv6/xfrm6_state.c | 33 | ||||
| -rw-r--r-- | net/xfrm/xfrm_policy.c | 5 | ||||
| -rw-r--r-- | net/xfrm/xfrm_state.c | 45 |
5 files changed, 69 insertions, 51 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h index fc8f36dd0f5c..4f53532d4c2f 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h | |||
| @@ -298,8 +298,8 @@ struct xfrm_state_afinfo { | |||
| 298 | const struct xfrm_type *type_map[IPPROTO_MAX]; | 298 | const struct xfrm_type *type_map[IPPROTO_MAX]; |
| 299 | struct xfrm_mode *mode_map[XFRM_MODE_MAX]; | 299 | struct xfrm_mode *mode_map[XFRM_MODE_MAX]; |
| 300 | int (*init_flags)(struct xfrm_state *x); | 300 | int (*init_flags)(struct xfrm_state *x); |
| 301 | void (*init_tempsel)(struct xfrm_state *x, struct flowi *fl, | 301 | void (*init_tempsel)(struct xfrm_selector *sel, struct flowi *fl); |
| 302 | struct xfrm_tmpl *tmpl, | 302 | void (*init_temprop)(struct xfrm_state *x, struct xfrm_tmpl *tmpl, |
| 303 | xfrm_address_t *daddr, xfrm_address_t *saddr); | 303 | xfrm_address_t *daddr, xfrm_address_t *saddr); |
| 304 | int (*tmpl_sort)(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n); | 304 | int (*tmpl_sort)(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n); |
| 305 | int (*state_sort)(struct xfrm_state **dst, struct xfrm_state **src, int n); | 305 | int (*state_sort)(struct xfrm_state **dst, struct xfrm_state **src, int n); |
diff --git a/net/ipv4/xfrm4_state.c b/net/ipv4/xfrm4_state.c index 1ef1366a0a03..47947624eccc 100644 --- a/net/ipv4/xfrm4_state.c +++ b/net/ipv4/xfrm4_state.c | |||
| @@ -21,21 +21,25 @@ static int xfrm4_init_flags(struct xfrm_state *x) | |||
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | static void | 23 | static void |
| 24 | __xfrm4_init_tempsel(struct xfrm_state *x, struct flowi *fl, | 24 | __xfrm4_init_tempsel(struct xfrm_selector *sel, struct flowi *fl) |
| 25 | struct xfrm_tmpl *tmpl, | 25 | { |
| 26 | xfrm_address_t *daddr, xfrm_address_t *saddr) | 26 | sel->daddr.a4 = fl->fl4_dst; |
| 27 | sel->saddr.a4 = fl->fl4_src; | ||
| 28 | sel->dport = xfrm_flowi_dport(fl); | ||
| 29 | sel->dport_mask = htons(0xffff); | ||
| 30 | sel->sport = xfrm_flowi_sport(fl); | ||
| 31 | sel->sport_mask = htons(0xffff); | ||
| 32 | sel->family = AF_INET; | ||
| 33 | sel->prefixlen_d = 32; | ||
| 34 | sel->prefixlen_s = 32; | ||
| 35 | sel->proto = fl->proto; | ||
| 36 | sel->ifindex = fl->oif; | ||
| 37 | } | ||
| 38 | |||
| 39 | static void | ||
| 40 | xfrm4_init_temprop(struct xfrm_state *x, struct xfrm_tmpl *tmpl, | ||
| 41 | xfrm_address_t *daddr, xfrm_address_t *saddr) | ||
| 27 | { | 42 | { |
| 28 | x->sel.daddr.a4 = fl->fl4_dst; | ||
| 29 | x->sel.saddr.a4 = fl->fl4_src; | ||
| 30 | x->sel.dport = xfrm_flowi_dport(fl); | ||
| 31 | x->sel.dport_mask = htons(0xffff); | ||
| 32 | x->sel.sport = xfrm_flowi_sport(fl); | ||
| 33 | x->sel.sport_mask = htons(0xffff); | ||
| 34 | x->sel.family = AF_INET; | ||
| 35 | x->sel.prefixlen_d = 32; | ||
| 36 | x->sel.prefixlen_s = 32; | ||
| 37 | x->sel.proto = fl->proto; | ||
| 38 | x->sel.ifindex = fl->oif; | ||
| 39 | x->id = tmpl->id; | 43 | x->id = tmpl->id; |
| 40 | if (x->id.daddr.a4 == 0) | 44 | if (x->id.daddr.a4 == 0) |
| 41 | x->id.daddr.a4 = daddr->a4; | 45 | x->id.daddr.a4 = daddr->a4; |
| @@ -70,6 +74,7 @@ static struct xfrm_state_afinfo xfrm4_state_afinfo = { | |||
| 70 | .owner = THIS_MODULE, | 74 | .owner = THIS_MODULE, |
| 71 | .init_flags = xfrm4_init_flags, | 75 | .init_flags = xfrm4_init_flags, |
| 72 | .init_tempsel = __xfrm4_init_tempsel, | 76 | .init_tempsel = __xfrm4_init_tempsel, |
| 77 | .init_temprop = xfrm4_init_temprop, | ||
| 73 | .output = xfrm4_output, | 78 | .output = xfrm4_output, |
| 74 | .extract_input = xfrm4_extract_input, | 79 | .extract_input = xfrm4_extract_input, |
| 75 | .extract_output = xfrm4_extract_output, | 80 | .extract_output = xfrm4_extract_output, |
diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c index f417b77fa0e1..a67575d472a3 100644 --- a/net/ipv6/xfrm6_state.c +++ b/net/ipv6/xfrm6_state.c | |||
| @@ -20,23 +20,27 @@ | |||
| 20 | #include <net/addrconf.h> | 20 | #include <net/addrconf.h> |
| 21 | 21 | ||
| 22 | static void | 22 | static void |
| 23 | __xfrm6_init_tempsel(struct xfrm_state *x, struct flowi *fl, | 23 | __xfrm6_init_tempsel(struct xfrm_selector *sel, struct flowi *fl) |
| 24 | struct xfrm_tmpl *tmpl, | ||
| 25 | xfrm_address_t *daddr, xfrm_address_t *saddr) | ||
| 26 | { | 24 | { |
| 27 | /* Initialize temporary selector matching only | 25 | /* Initialize temporary selector matching only |
| 28 | * to current session. */ | 26 | * to current session. */ |
| 29 | ipv6_addr_copy((struct in6_addr *)&x->sel.daddr, &fl->fl6_dst); | 27 | ipv6_addr_copy((struct in6_addr *)&sel->daddr, &fl->fl6_dst); |
| 30 | ipv6_addr_copy((struct in6_addr *)&x->sel.saddr, &fl->fl6_src); | 28 | ipv6_addr_copy((struct in6_addr *)&sel->saddr, &fl->fl6_src); |
| 31 | x->sel.dport = xfrm_flowi_dport(fl); | 29 | sel->dport = xfrm_flowi_dport(fl); |
| 32 | x->sel.dport_mask = htons(0xffff); | 30 | sel->dport_mask = htons(0xffff); |
| 33 | x->sel.sport = xfrm_flowi_sport(fl); | 31 | sel->sport = xfrm_flowi_sport(fl); |
| 34 | x->sel.sport_mask = htons(0xffff); | 32 | sel->sport_mask = htons(0xffff); |
| 35 | x->sel.family = AF_INET6; | 33 | sel->family = AF_INET6; |
| 36 | x->sel.prefixlen_d = 128; | 34 | sel->prefixlen_d = 128; |
| 37 | x->sel.prefixlen_s = 128; | 35 | sel->prefixlen_s = 128; |
| 38 | x->sel.proto = fl->proto; | 36 | sel->proto = fl->proto; |
| 39 | x->sel.ifindex = fl->oif; | 37 | sel->ifindex = fl->oif; |
| 38 | } | ||
| 39 | |||
| 40 | static void | ||
| 41 | xfrm6_init_temprop(struct xfrm_state *x, struct xfrm_tmpl *tmpl, | ||
| 42 | xfrm_address_t *daddr, xfrm_address_t *saddr) | ||
| 43 | { | ||
| 40 | x->id = tmpl->id; | 44 | x->id = tmpl->id; |
| 41 | if (ipv6_addr_any((struct in6_addr*)&x->id.daddr)) | 45 | if (ipv6_addr_any((struct in6_addr*)&x->id.daddr)) |
| 42 | memcpy(&x->id.daddr, daddr, sizeof(x->sel.daddr)); | 46 | memcpy(&x->id.daddr, daddr, sizeof(x->sel.daddr)); |
| @@ -168,6 +172,7 @@ static struct xfrm_state_afinfo xfrm6_state_afinfo = { | |||
| 168 | .eth_proto = htons(ETH_P_IPV6), | 172 | .eth_proto = htons(ETH_P_IPV6), |
| 169 | .owner = THIS_MODULE, | 173 | .owner = THIS_MODULE, |
| 170 | .init_tempsel = __xfrm6_init_tempsel, | 174 | .init_tempsel = __xfrm6_init_tempsel, |
| 175 | .init_temprop = xfrm6_init_temprop, | ||
| 171 | .tmpl_sort = __xfrm6_tmpl_sort, | 176 | .tmpl_sort = __xfrm6_tmpl_sort, |
| 172 | .state_sort = __xfrm6_state_sort, | 177 | .state_sort = __xfrm6_state_sort, |
| 173 | .output = xfrm6_output, | 178 | .output = xfrm6_output, |
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 2b3ed7ad4933..cbab6e1a8c9c 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c | |||
| @@ -1175,9 +1175,8 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl, | |||
| 1175 | tmpl->mode == XFRM_MODE_BEET) { | 1175 | tmpl->mode == XFRM_MODE_BEET) { |
| 1176 | remote = &tmpl->id.daddr; | 1176 | remote = &tmpl->id.daddr; |
| 1177 | local = &tmpl->saddr; | 1177 | local = &tmpl->saddr; |
| 1178 | family = tmpl->encap_family; | 1178 | if (xfrm_addr_any(local, tmpl->encap_family)) { |
| 1179 | if (xfrm_addr_any(local, family)) { | 1179 | error = xfrm_get_saddr(net, &tmp, remote, tmpl->encap_family); |
| 1180 | error = xfrm_get_saddr(net, &tmp, remote, family); | ||
| 1181 | if (error) | 1180 | if (error) |
| 1182 | goto fail; | 1181 | goto fail; |
| 1183 | local = &tmp; | 1182 | local = &tmp; |
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 5208b12fbfb4..eb96ce52f178 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c | |||
| @@ -656,15 +656,23 @@ void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si) | |||
| 656 | EXPORT_SYMBOL(xfrm_sad_getinfo); | 656 | EXPORT_SYMBOL(xfrm_sad_getinfo); |
| 657 | 657 | ||
| 658 | static int | 658 | static int |
| 659 | xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl, | 659 | xfrm_init_tempstate(struct xfrm_state *x, struct flowi *fl, |
| 660 | struct xfrm_tmpl *tmpl, | 660 | struct xfrm_tmpl *tmpl, |
| 661 | xfrm_address_t *daddr, xfrm_address_t *saddr, | 661 | xfrm_address_t *daddr, xfrm_address_t *saddr, |
| 662 | unsigned short family) | 662 | unsigned short family) |
| 663 | { | 663 | { |
| 664 | struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); | 664 | struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); |
| 665 | if (!afinfo) | 665 | if (!afinfo) |
| 666 | return -1; | 666 | return -1; |
| 667 | afinfo->init_tempsel(x, fl, tmpl, daddr, saddr); | 667 | afinfo->init_tempsel(&x->sel, fl); |
| 668 | |||
| 669 | if (family != tmpl->encap_family) { | ||
| 670 | xfrm_state_put_afinfo(afinfo); | ||
| 671 | afinfo = xfrm_state_get_afinfo(tmpl->encap_family); | ||
| 672 | if (!afinfo) | ||
| 673 | return -1; | ||
| 674 | } | ||
| 675 | afinfo->init_temprop(x, tmpl, daddr, saddr); | ||
| 668 | xfrm_state_put_afinfo(afinfo); | 676 | xfrm_state_put_afinfo(afinfo); |
| 669 | return 0; | 677 | return 0; |
| 670 | } | 678 | } |
| @@ -790,37 +798,38 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, | |||
| 790 | int error = 0; | 798 | int error = 0; |
| 791 | struct xfrm_state *best = NULL; | 799 | struct xfrm_state *best = NULL; |
| 792 | u32 mark = pol->mark.v & pol->mark.m; | 800 | u32 mark = pol->mark.v & pol->mark.m; |
| 801 | unsigned short encap_family = tmpl->encap_family; | ||
| 793 | 802 | ||
| 794 | to_put = NULL; | 803 | to_put = NULL; |
| 795 | 804 | ||
| 796 | spin_lock_bh(&xfrm_state_lock); | 805 | spin_lock_bh(&xfrm_state_lock); |
| 797 | h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, family); | 806 | h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family); |
| 798 | hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) { | 807 | hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) { |
| 799 | if (x->props.family == family && | 808 | if (x->props.family == encap_family && |
| 800 | x->props.reqid == tmpl->reqid && | 809 | x->props.reqid == tmpl->reqid && |
| 801 | (mark & x->mark.m) == x->mark.v && | 810 | (mark & x->mark.m) == x->mark.v && |
| 802 | !(x->props.flags & XFRM_STATE_WILDRECV) && | 811 | !(x->props.flags & XFRM_STATE_WILDRECV) && |
| 803 | xfrm_state_addr_check(x, daddr, saddr, family) && | 812 | xfrm_state_addr_check(x, daddr, saddr, encap_family) && |
| 804 | tmpl->mode == x->props.mode && | 813 | tmpl->mode == x->props.mode && |
| 805 | tmpl->id.proto == x->id.proto && | 814 | tmpl->id.proto == x->id.proto && |
| 806 | (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) | 815 | (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) |
| 807 | xfrm_state_look_at(pol, x, fl, family, daddr, saddr, | 816 | xfrm_state_look_at(pol, x, fl, encap_family, daddr, saddr, |
| 808 | &best, &acquire_in_progress, &error); | 817 | &best, &acquire_in_progress, &error); |
| 809 | } | 818 | } |
| 810 | if (best) | 819 | if (best) |
| 811 | goto found; | 820 | goto found; |
| 812 | 821 | ||
| 813 | h_wildcard = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, family); | 822 | h_wildcard = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, encap_family); |
| 814 | hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h_wildcard, bydst) { | 823 | hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h_wildcard, bydst) { |
| 815 | if (x->props.family == family && | 824 | if (x->props.family == encap_family && |
| 816 | x->props.reqid == tmpl->reqid && | 825 | x->props.reqid == tmpl->reqid && |
| 817 | (mark & x->mark.m) == x->mark.v && | 826 | (mark & x->mark.m) == x->mark.v && |
| 818 | !(x->props.flags & XFRM_STATE_WILDRECV) && | 827 | !(x->props.flags & XFRM_STATE_WILDRECV) && |
| 819 | xfrm_state_addr_check(x, daddr, saddr, family) && | 828 | xfrm_state_addr_check(x, daddr, saddr, encap_family) && |
| 820 | tmpl->mode == x->props.mode && | 829 | tmpl->mode == x->props.mode && |
| 821 | tmpl->id.proto == x->id.proto && | 830 | tmpl->id.proto == x->id.proto && |
| 822 | (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) | 831 | (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) |
| 823 | xfrm_state_look_at(pol, x, fl, family, daddr, saddr, | 832 | xfrm_state_look_at(pol, x, fl, encap_family, daddr, saddr, |
| 824 | &best, &acquire_in_progress, &error); | 833 | &best, &acquire_in_progress, &error); |
| 825 | } | 834 | } |
| 826 | 835 | ||
| @@ -829,7 +838,7 @@ found: | |||
| 829 | if (!x && !error && !acquire_in_progress) { | 838 | if (!x && !error && !acquire_in_progress) { |
| 830 | if (tmpl->id.spi && | 839 | if (tmpl->id.spi && |
| 831 | (x0 = __xfrm_state_lookup(net, mark, daddr, tmpl->id.spi, | 840 | (x0 = __xfrm_state_lookup(net, mark, daddr, tmpl->id.spi, |
| 832 | tmpl->id.proto, family)) != NULL) { | 841 | tmpl->id.proto, encap_family)) != NULL) { |
| 833 | to_put = x0; | 842 | to_put = x0; |
| 834 | error = -EEXIST; | 843 | error = -EEXIST; |
| 835 | goto out; | 844 | goto out; |
| @@ -839,9 +848,9 @@ found: | |||
| 839 | error = -ENOMEM; | 848 | error = -ENOMEM; |
| 840 | goto out; | 849 | goto out; |
| 841 | } | 850 | } |
| 842 | /* Initialize temporary selector matching only | 851 | /* Initialize temporary state matching only |
| 843 | * to current session. */ | 852 | * to current session. */ |
| 844 | xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family); | 853 | xfrm_init_tempstate(x, fl, tmpl, daddr, saddr, family); |
| 845 | memcpy(&x->mark, &pol->mark, sizeof(x->mark)); | 854 | memcpy(&x->mark, &pol->mark, sizeof(x->mark)); |
| 846 | 855 | ||
| 847 | error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid); | 856 | error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid); |
| @@ -856,10 +865,10 @@ found: | |||
| 856 | x->km.state = XFRM_STATE_ACQ; | 865 | x->km.state = XFRM_STATE_ACQ; |
| 857 | list_add(&x->km.all, &net->xfrm.state_all); | 866 | list_add(&x->km.all, &net->xfrm.state_all); |
| 858 | hlist_add_head(&x->bydst, net->xfrm.state_bydst+h); | 867 | hlist_add_head(&x->bydst, net->xfrm.state_bydst+h); |
| 859 | h = xfrm_src_hash(net, daddr, saddr, family); | 868 | h = xfrm_src_hash(net, daddr, saddr, encap_family); |
| 860 | hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h); | 869 | hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h); |
| 861 | if (x->id.spi) { | 870 | if (x->id.spi) { |
| 862 | h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, family); | 871 | h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, encap_family); |
| 863 | hlist_add_head(&x->byspi, net->xfrm.state_byspi+h); | 872 | hlist_add_head(&x->byspi, net->xfrm.state_byspi+h); |
| 864 | } | 873 | } |
| 865 | x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires; | 874 | x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires; |
