@@ -838,7 +838,7 @@ __be16 xfrm_flowi_dport(struct flowi *fl)
}
extern int xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl,
- unsigned short family);
+ unsigned short family, int use_if);
#ifdef CONFIG_SECURITY_NETWORK_XFRM
/* If neither has a context --> match
@@ -55,35 +55,37 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
int dir);
static inline int
-__xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl)
+__xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl, int uif)
{
+ int use_if = uif ? uif : fl->oif;
return addr_match(&fl->fl4_dst, &sel->daddr, sel->prefixlen_d) &&
addr_match(&fl->fl4_src, &sel->saddr, sel->prefixlen_s) &&
!((xfrm_flowi_dport(fl) ^ sel->dport) & sel->dport_mask) &&
!((xfrm_flowi_sport(fl) ^ sel->sport) & sel->sport_mask) &&
(fl->proto == sel->proto || !sel->proto) &&
- (fl->oif == sel->ifindex || !sel->ifindex);
+ (use_if == sel->ifindex || !sel->ifindex);
}
static inline int
-__xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl)
+__xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl, int uif)
{
+ int use_if = uif ? uif : fl->oif;
return addr_match(&fl->fl6_dst, &sel->daddr, sel->prefixlen_d) &&
addr_match(&fl->fl6_src, &sel->saddr, sel->prefixlen_s) &&
!((xfrm_flowi_dport(fl) ^ sel->dport) & sel->dport_mask) &&
!((xfrm_flowi_sport(fl) ^ sel->sport) & sel->sport_mask) &&
(fl->proto == sel->proto || !sel->proto) &&
- (fl->oif == sel->ifindex || !sel->ifindex);
+ (use_if == sel->ifindex || !sel->ifindex);
}
int xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl,
- unsigned short family)
+ unsigned short family, int ifindex)
{
switch (family) {
case AF_INET:
- return __xfrm4_selector_match(sel, fl);
+ return __xfrm4_selector_match(sel, fl, ifindex);
case AF_INET6:
- return __xfrm6_selector_match(sel, fl);
+ return __xfrm6_selector_match(sel, fl, ifindex);
}
return 0;
}
@@ -917,14 +919,17 @@ static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl,
u8 type, u16 family, int dir)
{
struct xfrm_selector *sel = &pol->selector;
- int match, ret = -ESRCH;
+ int use_if = 0, match, ret = -ESRCH;
if (pol->family != family ||
(fl->mark & pol->mark.m) != pol->mark.v ||
pol->type != type)
return ret;
- match = xfrm_selector_match(sel, fl, family);
+ if (dir == FLOW_DIR_IN || dir == FLOW_DIR_FWD)
+ use_if = fl->iif;
+
+ match = xfrm_selector_match(sel, fl, family, use_if);
if (match)
ret = security_xfrm_policy_lookup(pol->security, fl->secid,
dir);
@@ -1041,7 +1046,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struc
read_lock_bh(&xfrm_policy_lock);
if ((pol = sk->sk_policy[dir]) != NULL) {
int match = xfrm_selector_match(&pol->selector, fl,
- sk->sk_family);
+ sk->sk_family, 0);
int err = 0;
if (match) {
@@ -1918,6 +1923,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
struct flowi fl;
u8 fl_dir;
int xerr_idx = -1;
+ int use_if = 0;
reverse = dir & ~XFRM_POLICY_MASK;
dir &= XFRM_POLICY_MASK;
@@ -1928,6 +1934,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
return 0;
}
+ if (fl_dir == FLOW_DIR_IN || fl_dir == FLOW_DIR_FWD)
+ fl.iif = use_if = skb->skb_iif;
+
nf_nat_decode_session(skb, &fl, family);
/* First, check used SA against their selectors. */
@@ -1936,7 +1945,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
for (i=skb->sp->len-1; i>=0; i--) {
struct xfrm_state *x = skb->sp->xvec[i];
- if (!xfrm_selector_match(&x->sel, &fl, family)) {
+ if (!xfrm_selector_match(&x->sel, &fl, family, use_if)) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMISMATCH);
return 0;
}
@@ -2243,7 +2252,7 @@ int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
if (first->origin && !flow_cache_uli_match(first->origin, fl))
return 0;
if (first->partner &&
- !xfrm_selector_match(first->partner, fl, family))
+ !xfrm_selector_match(first->partner, fl, family, 0))
return 0;
}
#endif
@@ -2253,7 +2262,7 @@ int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
do {
struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
- if (fl && !xfrm_selector_match(&dst->xfrm->sel, fl, family))
+ if (fl && !xfrm_selector_match(&dst->xfrm->sel, fl, family, 0))
return 0;
if (fl && pol &&
!security_xfrm_state_pol_flow_match(dst->xfrm, pol, fl))
@@ -756,7 +756,7 @@ static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x,
*/
if (x->km.state == XFRM_STATE_VALID) {
if ((x->sel.family &&
- !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
+ !xfrm_selector_match(&x->sel, fl, x->sel.family, 0)) ||
!security_xfrm_state_pol_flow_match(x, pol, fl))
return;
@@ -769,7 +769,7 @@ static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x,
*acq_in_progress = 1;
} else if (x->km.state == XFRM_STATE_ERROR ||
x->km.state == XFRM_STATE_EXPIRED) {
- if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
+ if (xfrm_selector_match(&x->sel, fl, x->sel.family, 0) &&
security_xfrm_state_pol_flow_match(x, pol, fl))
*error = -ESRCH;
}