[XFRM] IPV6: Restrict bundle reusing
authorMasahide NAKAMURA <nakam@linux-ipv6.org>
Thu, 24 Aug 2006 02:12:01 +0000 (19:12 -0700)
committerDavid S. Miller <davem@sunset.davemloft.net>
Fri, 22 Sep 2006 22:06:44 +0000 (15:06 -0700)
For outbound transformation, bundle is checked whether it is
suitable for current flow to be reused or not. In such IPv6 case
as below, transformation may apply incorrect bundle for the flow instead
of creating another bundle:

- The policy selector has destination prefix length < 128
  (Two or more addresses can be matched it)
- Its bundle holds dst entry of default route whose prefix length < 128
  (Previous traffic was used such route as next hop)
- The policy and the bundle were used a transport mode state and
  this time flow address is not matched the bundled state.

This issue is found by Mobile IPv6 usage to protect mobility signaling
by IPsec, but it is not a Mobile IPv6 specific.
This patch adds strict check to xfrm_bundle_ok() for each
state mode and address when prefix length is less than 128.

Signed-off-by: Masahide NAKAMURA <nakam@linux-ipv6.org>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/xfrm.h
net/ipv4/xfrm4_policy.c
net/ipv6/xfrm6_policy.c
net/xfrm/xfrm_policy.c

index 248874ecf8dff59f1f602638d7465112aa7be015..7f1630630dcf3dfd17c1a3a9773865dd7820dd03 100644 (file)
@@ -869,6 +869,23 @@ xfrm_state_addr_check(struct xfrm_state *x,
        return 0;
 }
 
+static __inline__ int
+xfrm_state_addr_flow_check(struct xfrm_state *x, struct flowi *fl,
+                          unsigned short family)
+{
+       switch (family) {
+       case AF_INET:
+               return __xfrm4_state_addr_check(x,
+                                               (xfrm_address_t *)&fl->fl4_dst,
+                                               (xfrm_address_t *)&fl->fl4_src);
+       case AF_INET6:
+               return __xfrm6_state_addr_check(x,
+                                               (xfrm_address_t *)&fl->fl6_dst,
+                                               (xfrm_address_t *)&fl->fl6_src);
+       }
+       return 0;
+}
+
 static inline int xfrm_state_kern(struct xfrm_state *x)
 {
        return atomic_read(&x->tunnel_users);
@@ -1014,7 +1031,7 @@ extern void xfrm_policy_flush(void);
 extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol);
 extern int xfrm_flush_bundles(void);
 extern void xfrm_flush_all_bundles(void);
-extern int xfrm_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl, int family);
+extern int xfrm_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl, int family, int strict);
 extern void xfrm_init_pmtu(struct dst_entry *dst);
 
 extern wait_queue_head_t km_waitq;
index e517981ceadd4f5a2fe170e7761668c1cb68cb12..42d8ded0f96a81116dd624dd337901bb18b8f0a8 100644 (file)
@@ -33,7 +33,7 @@ __xfrm4_find_bundle(struct flowi *fl, struct xfrm_policy *policy)
                    xdst->u.rt.fl.fl4_dst == fl->fl4_dst &&
                    xdst->u.rt.fl.fl4_src == fl->fl4_src &&
                    xdst->u.rt.fl.fl4_tos == fl->fl4_tos &&
-                   xfrm_bundle_ok(xdst, fl, AF_INET)) {
+                   xfrm_bundle_ok(xdst, fl, AF_INET, 0)) {
                        dst_clone(dst);
                        break;
                }
index a3f68c8b737e866a42434a890b9dacec9a26fb3e..729b4748d6d3df45e361b5603b62a849d392a025 100644 (file)
@@ -50,7 +50,9 @@ __xfrm6_find_bundle(struct flowi *fl, struct xfrm_policy *policy)
                                 xdst->u.rt6.rt6i_src.plen);
                if (ipv6_addr_equal(&xdst->u.rt6.rt6i_dst.addr, &fl_dst_prefix) &&
                    ipv6_addr_equal(&xdst->u.rt6.rt6i_src.addr, &fl_src_prefix) &&
-                   xfrm_bundle_ok(xdst, fl, AF_INET6)) {
+                   xfrm_bundle_ok(xdst, fl, AF_INET6,
+                                  (xdst->u.rt6.rt6i_dst.plen != 128 ||
+                                   xdst->u.rt6.rt6i_src.plen != 128))) {
                        dst_clone(dst);
                        break;
                }
index 56abb5c057d4ace5bfd5d7141fa95e130fc67215..ad2a5cba1f5b006686725c5ca38734339dc21ead 100644 (file)
@@ -1167,7 +1167,7 @@ static struct dst_entry *xfrm_dst_check(struct dst_entry *dst, u32 cookie)
 
 static int stale_bundle(struct dst_entry *dst)
 {
-       return !xfrm_bundle_ok((struct xfrm_dst *)dst, NULL, AF_UNSPEC);
+       return !xfrm_bundle_ok((struct xfrm_dst *)dst, NULL, AF_UNSPEC, 0);
 }
 
 void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev)
@@ -1282,7 +1282,7 @@ EXPORT_SYMBOL(xfrm_init_pmtu);
  * still valid.
  */
 
-int xfrm_bundle_ok(struct xfrm_dst *first, struct flowi *fl, int family)
+int xfrm_bundle_ok(struct xfrm_dst *first, struct flowi *fl, int family, int strict)
 {
        struct dst_entry *dst = &first->u.dst;
        struct xfrm_dst *last;
@@ -1304,6 +1304,10 @@ int xfrm_bundle_ok(struct xfrm_dst *first, struct flowi *fl, int family)
                if (dst->xfrm->km.state != XFRM_STATE_VALID)
                        return 0;
 
+               if (strict && fl && dst->xfrm->props.mode != XFRM_MODE_TUNNEL &&
+                   !xfrm_state_addr_flow_check(dst->xfrm, fl, family))
+                       return 0;
+
                mtu = dst_mtu(dst->child);
                if (xdst->child_mtu_cached != mtu) {
                        last = xdst;