Skip to content

Commit

Permalink
xfrm: fix "disable_policy" flag use when arriving from different devices
Browse files Browse the repository at this point in the history
[ Upstream commit e6175a2 ]

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 <shmulik.ladkani@gmail.com>
Signed-off-by: Eyal Birger <eyal.birger@gmail.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
ebirger authored and gregkh committed May 25, 2022
1 parent 0d2e9d8 commit 47f04f9
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 6 deletions.
1 change: 1 addition & 0 deletions include/net/ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ struct inet_skb_parm {
#define IPSKB_DOREDIRECT BIT(5)
#define IPSKB_FRAG_PMTU BIT(6)
#define IPSKB_L3SLAVE BIT(7)
#define IPSKB_NOPOLICY BIT(8)

u16 frag_max_size;
};
Expand Down
14 changes: 13 additions & 1 deletion include/net/xfrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,18 @@ static inline bool __xfrm_check_nopolicy(struct net *net, struct sk_buff *skb,
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,
struct sk_buff *skb,
unsigned int family, int reverse)
Expand All @@ -1111,7 +1123,7 @@ static inline int __xfrm_policy_check2(struct sock *sk, int dir,
return __xfrm_policy_check(sk, ndir, skb, family);

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);
}

Expand Down
23 changes: 18 additions & 5 deletions net/ipv4/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -1765,6 +1765,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);
unsigned int flags = RTCF_MULTICAST;
struct rtable *rth;
bool no_policy;
u32 itag = 0;
int err;

Expand All @@ -1775,8 +1776,12 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
if (our)
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,
IN_DEV_ORCONF(in_dev, NOPOLICY), false);
no_policy, false);
if (!rth)
return -ENOBUFS;

Expand Down Expand Up @@ -1835,7 +1840,7 @@ static int __mkroute_input(struct sk_buff *skb,
struct rtable *rth;
int err;
struct in_device *out_dev;
bool do_cache;
bool do_cache, no_policy;
u32 itag = 0;

/* get a working reference to the output device */
Expand Down Expand Up @@ -1880,6 +1885,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);
if (do_cache) {
if (fnhe)
Expand All @@ -1892,8 +1901,7 @@ static int __mkroute_input(struct sk_buff *skb,
}
}

rth = rt_dst_alloc(out_dev->dev, 0, res->type,
IN_DEV_ORCONF(in_dev, NOPOLICY),
rth = rt_dst_alloc(out_dev->dev, 0, res->type, no_policy,
IN_DEV_ORCONF(out_dev, NOXFRM));
if (!rth) {
err = -ENOBUFS;
Expand Down Expand Up @@ -2145,6 +2153,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
struct rtable *rth;
struct flowi4 fl4;
bool do_cache = true;
bool no_policy;

/* IP on this device is disabled. */

Expand Down Expand Up @@ -2262,6 +2271,10 @@ out: return err;
RT_CACHE_STAT_INC(in_brd);

local_input:
no_policy = IN_DEV_ORCONF(in_dev, NOPOLICY);
if (no_policy)
IPCB(skb)->flags |= IPSKB_NOPOLICY;

do_cache &= res->fi && !itag;
if (do_cache) {
struct fib_nh_common *nhc = FIB_RES_NHC(*res);
Expand All @@ -2276,7 +2289,7 @@ out: return err;

rth = rt_dst_alloc(ip_rt_get_dev(net, res),
flags | RTCF_LOCAL, res->type,
IN_DEV_ORCONF(in_dev, NOPOLICY), false);
no_policy, false);
if (!rth)
goto e_nobufs;

Expand Down

0 comments on commit 47f04f9

Please sign in to comment.