Skip to content

Commit e6175a2

Browse files
ebirgerklassert
authored andcommitted
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: 1da177e ("Linux-2.6.12-rc2") Reported-by: Shmulik Ladkani <[email protected]> Signed-off-by: Eyal Birger <[email protected]> Signed-off-by: Steffen Klassert <[email protected]>
1 parent 7939693 commit e6175a2

File tree

3 files changed

+32
-6
lines changed

3 files changed

+32
-6
lines changed

include/net/ip.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ struct inet_skb_parm {
5656
#define IPSKB_DOREDIRECT BIT(5)
5757
#define IPSKB_FRAG_PMTU BIT(6)
5858
#define IPSKB_L3SLAVE BIT(7)
59+
#define IPSKB_NOPOLICY BIT(8)
5960

6061
u16 frag_max_size;
6162
};

include/net/xfrm.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,18 @@ static inline bool __xfrm_check_nopolicy(struct net *net, struct sk_buff *skb,
10931093
return false;
10941094
}
10951095

1096+
static inline bool __xfrm_check_dev_nopolicy(struct sk_buff *skb,
1097+
int dir, unsigned short family)
1098+
{
1099+
if (dir != XFRM_POLICY_OUT && family == AF_INET) {
1100+
/* same dst may be used for traffic originating from
1101+
* devices with different policy settings.
1102+
*/
1103+
return IPCB(skb)->flags & IPSKB_NOPOLICY;
1104+
}
1105+
return skb_dst(skb) && (skb_dst(skb)->flags & DST_NOPOLICY);
1106+
}
1107+
10961108
static inline int __xfrm_policy_check2(struct sock *sk, int dir,
10971109
struct sk_buff *skb,
10981110
unsigned int family, int reverse)
@@ -1104,7 +1116,7 @@ static inline int __xfrm_policy_check2(struct sock *sk, int dir,
11041116
return __xfrm_policy_check(sk, ndir, skb, family);
11051117

11061118
return __xfrm_check_nopolicy(net, skb, dir) ||
1107-
(skb_dst(skb) && (skb_dst(skb)->flags & DST_NOPOLICY)) ||
1119+
__xfrm_check_dev_nopolicy(skb, dir, family) ||
11081120
__xfrm_policy_check(sk, ndir, skb, family);
11091121
}
11101122

net/ipv4/route.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,6 +1726,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
17261726
struct in_device *in_dev = __in_dev_get_rcu(dev);
17271727
unsigned int flags = RTCF_MULTICAST;
17281728
struct rtable *rth;
1729+
bool no_policy;
17291730
u32 itag = 0;
17301731
int err;
17311732

@@ -1736,8 +1737,12 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
17361737
if (our)
17371738
flags |= RTCF_LOCAL;
17381739

1740+
no_policy = IN_DEV_ORCONF(in_dev, NOPOLICY);
1741+
if (no_policy)
1742+
IPCB(skb)->flags |= IPSKB_NOPOLICY;
1743+
17391744
rth = rt_dst_alloc(dev_net(dev)->loopback_dev, flags, RTN_MULTICAST,
1740-
IN_DEV_ORCONF(in_dev, NOPOLICY), false);
1745+
no_policy, false);
17411746
if (!rth)
17421747
return -ENOBUFS;
17431748

@@ -1795,7 +1800,7 @@ static int __mkroute_input(struct sk_buff *skb,
17951800
struct rtable *rth;
17961801
int err;
17971802
struct in_device *out_dev;
1798-
bool do_cache;
1803+
bool do_cache, no_policy;
17991804
u32 itag = 0;
18001805

18011806
/* get a working reference to the output device */
@@ -1840,6 +1845,10 @@ static int __mkroute_input(struct sk_buff *skb,
18401845
}
18411846
}
18421847

1848+
no_policy = IN_DEV_ORCONF(in_dev, NOPOLICY);
1849+
if (no_policy)
1850+
IPCB(skb)->flags |= IPSKB_NOPOLICY;
1851+
18431852
fnhe = find_exception(nhc, daddr);
18441853
if (do_cache) {
18451854
if (fnhe)
@@ -1852,8 +1861,7 @@ static int __mkroute_input(struct sk_buff *skb,
18521861
}
18531862
}
18541863

1855-
rth = rt_dst_alloc(out_dev->dev, 0, res->type,
1856-
IN_DEV_ORCONF(in_dev, NOPOLICY),
1864+
rth = rt_dst_alloc(out_dev->dev, 0, res->type, no_policy,
18571865
IN_DEV_ORCONF(out_dev, NOXFRM));
18581866
if (!rth) {
18591867
err = -ENOBUFS;
@@ -2228,6 +2236,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
22282236
struct rtable *rth;
22292237
struct flowi4 fl4;
22302238
bool do_cache = true;
2239+
bool no_policy;
22312240

22322241
/* IP on this device is disabled. */
22332242

@@ -2346,6 +2355,10 @@ out: return err;
23462355
RT_CACHE_STAT_INC(in_brd);
23472356

23482357
local_input:
2358+
no_policy = IN_DEV_ORCONF(in_dev, NOPOLICY);
2359+
if (no_policy)
2360+
IPCB(skb)->flags |= IPSKB_NOPOLICY;
2361+
23492362
do_cache &= res->fi && !itag;
23502363
if (do_cache) {
23512364
struct fib_nh_common *nhc = FIB_RES_NHC(*res);
@@ -2360,7 +2373,7 @@ out: return err;
23602373

23612374
rth = rt_dst_alloc(ip_rt_get_dev(net, res),
23622375
flags | RTCF_LOCAL, res->type,
2363-
IN_DEV_ORCONF(in_dev, NOPOLICY), false);
2376+
no_policy, false);
23642377
if (!rth)
23652378
goto e_nobufs;
23662379

0 commit comments

Comments
 (0)