Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mfashe...
[deliverable/linux.git] / net / xfrm / xfrm_policy.c
index fffdd34f3bafbd3f707b22dc2e52ffc2c085df03..64d3938f74c46d1d13753c32a3b37da63fd27918 100644 (file)
@@ -50,6 +50,40 @@ static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo);
 static struct xfrm_policy_afinfo *xfrm_policy_lock_afinfo(unsigned int family);
 static void xfrm_policy_unlock_afinfo(struct xfrm_policy_afinfo *afinfo);
 
+static inline int
+__xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl)
+{
+       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);
+}
+
+static inline int
+__xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl)
+{
+       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);
+}
+
+int xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl,
+                   unsigned short family)
+{
+       switch (family) {
+       case AF_INET:
+               return __xfrm4_selector_match(sel, fl);
+       case AF_INET6:
+               return __xfrm6_selector_match(sel, fl);
+       }
+       return 0;
+}
+
 int xfrm_register_type(struct xfrm_type *type, unsigned short family)
 {
        struct xfrm_policy_afinfo *afinfo = xfrm_policy_lock_afinfo(family);
@@ -1016,12 +1050,16 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struc
                                                sk->sk_family);
                int err = 0;
 
-               if (match)
-                 err = security_xfrm_policy_lookup(pol, fl->secid, policy_to_flow_dir(dir));
-
-               if (match && !err)
-                       xfrm_pol_hold(pol);
-               else
+               if (match) {
+                       err = security_xfrm_policy_lookup(pol, fl->secid,
+                                       policy_to_flow_dir(dir));
+                       if (!err)
+                               xfrm_pol_hold(pol);
+                       else if (err == -ESRCH)
+                               pol = NULL;
+                       else
+                               pol = ERR_PTR(err);
+               } else
                        pol = NULL;
        }
        read_unlock_bh(&xfrm_policy_lock);
@@ -1173,6 +1211,7 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl,
                if (tmpl->mode == XFRM_MODE_TUNNEL) {
                        remote = &tmpl->id.daddr;
                        local = &tmpl->saddr;
+                       family = tmpl->encap_family;
                        if (xfrm_addr_any(local, family)) {
                                error = xfrm_get_saddr(&tmp, remote, family);
                                if (error)
@@ -1313,8 +1352,11 @@ restart:
        pol_dead = 0;
        xfrm_nr = 0;
 
-       if (sk && sk->sk_policy[1])
+       if (sk && sk->sk_policy[1]) {
                policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
+               if (IS_ERR(policy))
+                       return PTR_ERR(policy);
+       }
 
        if (!policy) {
                /* To accelerate a bit...  */
@@ -1607,8 +1649,11 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
        }
 
        pol = NULL;
-       if (sk && sk->sk_policy[dir])
+       if (sk && sk->sk_policy[dir]) {
                pol = xfrm_sk_policy_lookup(sk, dir, &fl);
+               if (IS_ERR(pol))
+                       return 0;
+       }
 
        if (!pol)
                pol = flow_cache_lookup(&fl, family, fl_dir,
@@ -1744,7 +1789,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, 0);
+       return !xfrm_bundle_ok(NULL, (struct xfrm_dst *)dst, NULL, AF_UNSPEC, 0);
 }
 
 void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev)
@@ -1866,7 +1911,8 @@ EXPORT_SYMBOL(xfrm_init_pmtu);
  * still valid.
  */
 
-int xfrm_bundle_ok(struct xfrm_dst *first, struct flowi *fl, int family, int strict)
+int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
+               struct flowi *fl, int family, int strict)
 {
        struct dst_entry *dst = &first->u.dst;
        struct xfrm_dst *last;
@@ -1883,7 +1929,8 @@ int xfrm_bundle_ok(struct xfrm_dst *first, struct flowi *fl, int family, int str
 
                if (fl && !xfrm_selector_match(&dst->xfrm->sel, fl, family))
                        return 0;
-               if (fl && !security_xfrm_flow_state_match(fl, dst->xfrm))
+               if (fl && pol &&
+                   !security_xfrm_state_pol_flow_match(dst->xfrm, pol, fl))
                        return 0;
                if (dst->xfrm->km.state != XFRM_STATE_VALID)
                        return 0;
This page took 0.025282 seconds and 5 git commands to generate.