forked from mirrors/linux
		
	xfrm: fix "disable_policy" flag use when arriving from different devices
In IPv4 setting the "disable_policy" flag on a device means no policy
should be enforced for traffic originating from the device. This was
implemented by seting the DST_NOPOLICY flag in the dst based on the
originating device.
However, dsts are cached in nexthops regardless of the originating
devices, in which case, the DST_NOPOLICY flag value may be incorrect.
Consider the following setup:
                     +------------------------------+
                     | ROUTER                       |
  +-------------+    | +-----------------+          |
  | ipsec src   |----|-|ipsec0           |          |
  +-------------+    | |disable_policy=0 |   +----+ |
                     | +-----------------+   |eth1|-|-----
  +-------------+    | +-----------------+   +----+ |
  | noipsec src |----|-|eth0             |          |
  +-------------+    | |disable_policy=1 |          |
                     | +-----------------+          |
                     +------------------------------+
Where ROUTER has a default route towards eth1.
dst entries for traffic arriving from eth0 would have DST_NOPOLICY
and would be cached and therefore can be reused by traffic originating
from ipsec0, skipping policy check.
Fix by setting a IPSKB_NOPOLICY flag in IPCB and observing it instead
of the DST in IN/FWD IPv4 policy checks.
Fixes: 1da177e4c3 ("Linux-2.6.12-rc2")
Reported-by: Shmulik Ladkani <shmulik.ladkani@gmail.com>
Signed-off-by: Eyal Birger <eyal.birger@gmail.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
			
			
This commit is contained in:
		
							parent
							
								
									79396934e2
								
							
						
					
					
						commit
						e6175a2ed1
					
				
					 3 changed files with 32 additions and 6 deletions
				
			
		|  | @ -56,6 +56,7 @@ struct inet_skb_parm { | ||||||
| #define IPSKB_DOREDIRECT	BIT(5) | #define IPSKB_DOREDIRECT	BIT(5) | ||||||
| #define IPSKB_FRAG_PMTU		BIT(6) | #define IPSKB_FRAG_PMTU		BIT(6) | ||||||
| #define IPSKB_L3SLAVE		BIT(7) | #define IPSKB_L3SLAVE		BIT(7) | ||||||
|  | #define IPSKB_NOPOLICY		BIT(8) | ||||||
| 
 | 
 | ||||||
| 	u16			frag_max_size; | 	u16			frag_max_size; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -1093,6 +1093,18 @@ static inline bool __xfrm_check_nopolicy(struct net *net, struct sk_buff *skb, | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline bool __xfrm_check_dev_nopolicy(struct sk_buff *skb, | ||||||
|  | 					     int dir, unsigned short family) | ||||||
|  | { | ||||||
|  | 	if (dir != XFRM_POLICY_OUT && family == AF_INET) { | ||||||
|  | 		/* same dst may be used for traffic originating from
 | ||||||
|  | 		 * devices with different policy settings. | ||||||
|  | 		 */ | ||||||
|  | 		return IPCB(skb)->flags & IPSKB_NOPOLICY; | ||||||
|  | 	} | ||||||
|  | 	return skb_dst(skb) && (skb_dst(skb)->flags & DST_NOPOLICY); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static inline int __xfrm_policy_check2(struct sock *sk, int dir, | static inline int __xfrm_policy_check2(struct sock *sk, int dir, | ||||||
| 				       struct sk_buff *skb, | 				       struct sk_buff *skb, | ||||||
| 				       unsigned int family, int reverse) | 				       unsigned int family, int reverse) | ||||||
|  | @ -1104,7 +1116,7 @@ static inline int __xfrm_policy_check2(struct sock *sk, int dir, | ||||||
| 		return __xfrm_policy_check(sk, ndir, skb, family); | 		return __xfrm_policy_check(sk, ndir, skb, family); | ||||||
| 
 | 
 | ||||||
| 	return __xfrm_check_nopolicy(net, skb, dir) || | 	return __xfrm_check_nopolicy(net, skb, dir) || | ||||||
| 	       (skb_dst(skb) && (skb_dst(skb)->flags & DST_NOPOLICY)) || | 	       __xfrm_check_dev_nopolicy(skb, dir, family) || | ||||||
| 	       __xfrm_policy_check(sk, ndir, skb, family); | 	       __xfrm_policy_check(sk, ndir, skb, family); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1726,6 +1726,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, | ||||||
| 	struct in_device *in_dev = __in_dev_get_rcu(dev); | 	struct in_device *in_dev = __in_dev_get_rcu(dev); | ||||||
| 	unsigned int flags = RTCF_MULTICAST; | 	unsigned int flags = RTCF_MULTICAST; | ||||||
| 	struct rtable *rth; | 	struct rtable *rth; | ||||||
|  | 	bool no_policy; | ||||||
| 	u32 itag = 0; | 	u32 itag = 0; | ||||||
| 	int err; | 	int err; | ||||||
| 
 | 
 | ||||||
|  | @ -1736,8 +1737,12 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, | ||||||
| 	if (our) | 	if (our) | ||||||
| 		flags |= RTCF_LOCAL; | 		flags |= RTCF_LOCAL; | ||||||
| 
 | 
 | ||||||
|  | 	no_policy = IN_DEV_ORCONF(in_dev, NOPOLICY); | ||||||
|  | 	if (no_policy) | ||||||
|  | 		IPCB(skb)->flags |= IPSKB_NOPOLICY; | ||||||
|  | 
 | ||||||
| 	rth = rt_dst_alloc(dev_net(dev)->loopback_dev, flags, RTN_MULTICAST, | 	rth = rt_dst_alloc(dev_net(dev)->loopback_dev, flags, RTN_MULTICAST, | ||||||
| 			   IN_DEV_ORCONF(in_dev, NOPOLICY), false); | 			   no_policy, false); | ||||||
| 	if (!rth) | 	if (!rth) | ||||||
| 		return -ENOBUFS; | 		return -ENOBUFS; | ||||||
| 
 | 
 | ||||||
|  | @ -1795,7 +1800,7 @@ static int __mkroute_input(struct sk_buff *skb, | ||||||
| 	struct rtable *rth; | 	struct rtable *rth; | ||||||
| 	int err; | 	int err; | ||||||
| 	struct in_device *out_dev; | 	struct in_device *out_dev; | ||||||
| 	bool do_cache; | 	bool do_cache, no_policy; | ||||||
| 	u32 itag = 0; | 	u32 itag = 0; | ||||||
| 
 | 
 | ||||||
| 	/* get a working reference to the output device */ | 	/* get a working reference to the output device */ | ||||||
|  | @ -1840,6 +1845,10 @@ static int __mkroute_input(struct sk_buff *skb, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	no_policy = IN_DEV_ORCONF(in_dev, NOPOLICY); | ||||||
|  | 	if (no_policy) | ||||||
|  | 		IPCB(skb)->flags |= IPSKB_NOPOLICY; | ||||||
|  | 
 | ||||||
| 	fnhe = find_exception(nhc, daddr); | 	fnhe = find_exception(nhc, daddr); | ||||||
| 	if (do_cache) { | 	if (do_cache) { | ||||||
| 		if (fnhe) | 		if (fnhe) | ||||||
|  | @ -1852,8 +1861,7 @@ static int __mkroute_input(struct sk_buff *skb, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	rth = rt_dst_alloc(out_dev->dev, 0, res->type, | 	rth = rt_dst_alloc(out_dev->dev, 0, res->type, no_policy, | ||||||
| 			   IN_DEV_ORCONF(in_dev, NOPOLICY), |  | ||||||
| 			   IN_DEV_ORCONF(out_dev, NOXFRM)); | 			   IN_DEV_ORCONF(out_dev, NOXFRM)); | ||||||
| 	if (!rth) { | 	if (!rth) { | ||||||
| 		err = -ENOBUFS; | 		err = -ENOBUFS; | ||||||
|  | @ -2228,6 +2236,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, | ||||||
| 	struct rtable	*rth; | 	struct rtable	*rth; | ||||||
| 	struct flowi4	fl4; | 	struct flowi4	fl4; | ||||||
| 	bool do_cache = true; | 	bool do_cache = true; | ||||||
|  | 	bool no_policy; | ||||||
| 
 | 
 | ||||||
| 	/* IP on this device is disabled. */ | 	/* IP on this device is disabled. */ | ||||||
| 
 | 
 | ||||||
|  | @ -2346,6 +2355,10 @@ out:	return err; | ||||||
| 	RT_CACHE_STAT_INC(in_brd); | 	RT_CACHE_STAT_INC(in_brd); | ||||||
| 
 | 
 | ||||||
| local_input: | local_input: | ||||||
|  | 	no_policy = IN_DEV_ORCONF(in_dev, NOPOLICY); | ||||||
|  | 	if (no_policy) | ||||||
|  | 		IPCB(skb)->flags |= IPSKB_NOPOLICY; | ||||||
|  | 
 | ||||||
| 	do_cache &= res->fi && !itag; | 	do_cache &= res->fi && !itag; | ||||||
| 	if (do_cache) { | 	if (do_cache) { | ||||||
| 		struct fib_nh_common *nhc = FIB_RES_NHC(*res); | 		struct fib_nh_common *nhc = FIB_RES_NHC(*res); | ||||||
|  | @ -2360,7 +2373,7 @@ out:	return err; | ||||||
| 
 | 
 | ||||||
| 	rth = rt_dst_alloc(ip_rt_get_dev(net, res), | 	rth = rt_dst_alloc(ip_rt_get_dev(net, res), | ||||||
| 			   flags | RTCF_LOCAL, res->type, | 			   flags | RTCF_LOCAL, res->type, | ||||||
| 			   IN_DEV_ORCONF(in_dev, NOPOLICY), false); | 			   no_policy, false); | ||||||
| 	if (!rth) | 	if (!rth) | ||||||
| 		goto e_nobufs; | 		goto e_nobufs; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Eyal Birger
						Eyal Birger