]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/xfrm/xfrm_state.c
Merge branch 'devel' of git://git.kernel.org/pub/scm/linux/kernel/git/ycmiao/pxa...
[linux-2.6-omap-h63xx.git] / net / xfrm / xfrm_state.c
index 2fd57f8f77c1ffc304ad4a0d65724c88f3495ff1..62a5425cc6aa1c83a386f2123281d4ccc07c9cdc 100644 (file)
@@ -748,12 +748,51 @@ static void xfrm_hash_grow_check(struct net *net, int have_hash_collision)
                schedule_work(&net->xfrm.state_hash_work);
 }
 
+static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x,
+                              struct flowi *fl, unsigned short family,
+                              xfrm_address_t *daddr, xfrm_address_t *saddr,
+                              struct xfrm_state **best, int *acq_in_progress,
+                              int *error)
+{
+       /* Resolution logic:
+        * 1. There is a valid state with matching selector. Done.
+        * 2. Valid state with inappropriate selector. Skip.
+        *
+        * Entering area of "sysdeps".
+        *
+        * 3. If state is not valid, selector is temporary, it selects
+        *    only session which triggered previous resolution. Key
+        *    manager will do something to install a state with proper
+        *    selector.
+        */
+       if (x->km.state == XFRM_STATE_VALID) {
+               if ((x->sel.family &&
+                    !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
+                   !security_xfrm_state_pol_flow_match(x, pol, fl))
+                       return;
+
+               if (!*best ||
+                   (*best)->km.dying > x->km.dying ||
+                   ((*best)->km.dying == x->km.dying &&
+                    (*best)->curlft.add_time < x->curlft.add_time))
+                       *best = x;
+       } else if (x->km.state == XFRM_STATE_ACQ) {
+               *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) &&
+                   security_xfrm_state_pol_flow_match(x, pol, fl))
+                       *error = -ESRCH;
+       }
+}
+
 struct xfrm_state *
 xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
                struct flowi *fl, struct xfrm_tmpl *tmpl,
                struct xfrm_policy *pol, int *err,
                unsigned short family)
 {
+       static xfrm_address_t saddr_wildcard = { };
        struct net *net = xp_net(pol);
        unsigned int h;
        struct hlist_node *entry;
@@ -773,40 +812,27 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
                    xfrm_state_addr_check(x, daddr, saddr, family) &&
                    tmpl->mode == x->props.mode &&
                    tmpl->id.proto == x->id.proto &&
-                   (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) {
-                       /* Resolution logic:
-                          1. There is a valid state with matching selector.
-                             Done.
-                          2. Valid state with inappropriate selector. Skip.
-
-                          Entering area of "sysdeps".
-
-                          3. If state is not valid, selector is temporary,
-                             it selects only session which triggered
-                             previous resolution. Key manager will do
-                             something to install a state with proper
-                             selector.
-                        */
-                       if (x->km.state == XFRM_STATE_VALID) {
-                               if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
-                                   !security_xfrm_state_pol_flow_match(x, pol, fl))
-                                       continue;
-                               if (!best ||
-                                   best->km.dying > x->km.dying ||
-                                   (best->km.dying == x->km.dying &&
-                                    best->curlft.add_time < x->curlft.add_time))
-                                       best = x;
-                       } else if (x->km.state == XFRM_STATE_ACQ) {
-                               acquire_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) &&
-                                   security_xfrm_state_pol_flow_match(x, pol, fl))
-                                       error = -ESRCH;
-                       }
-               }
+                   (tmpl->id.spi == x->id.spi || !tmpl->id.spi))
+                       xfrm_state_look_at(pol, x, fl, family, daddr, saddr,
+                                          &best, &acquire_in_progress, &error);
        }
+       if (best)
+               goto found;
 
+       h = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, family);
+       hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
+               if (x->props.family == family &&
+                   x->props.reqid == tmpl->reqid &&
+                   !(x->props.flags & XFRM_STATE_WILDRECV) &&
+                   xfrm_state_addr_check(x, daddr, saddr, family) &&
+                   tmpl->mode == x->props.mode &&
+                   tmpl->id.proto == x->id.proto &&
+                   (tmpl->id.spi == x->id.spi || !tmpl->id.spi))
+                       xfrm_state_look_at(pol, x, fl, family, daddr, saddr,
+                                          &best, &acquire_in_progress, &error);
+       }
+
+found:
        x = best;
        if (!x && !error && !acquire_in_progress) {
                if (tmpl->id.spi &&
@@ -2022,8 +2048,9 @@ int xfrm_init_state(struct xfrm_state *x)
                x->inner_mode = inner_mode;
        } else {
                struct xfrm_mode *inner_mode_iaf;
+               int iafamily = AF_INET;
 
-               inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
+               inner_mode = xfrm_get_mode(x->props.mode, x->props.family);
                if (inner_mode == NULL)
                        goto error;
 
@@ -2031,22 +2058,17 @@ int xfrm_init_state(struct xfrm_state *x)
                        xfrm_put_mode(inner_mode);
                        goto error;
                }
+               x->inner_mode = inner_mode;
 
-               inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
-               if (inner_mode_iaf == NULL)
-                       goto error;
-
-               if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
-                       xfrm_put_mode(inner_mode_iaf);
-                       goto error;
-               }
+               if (x->props.family == AF_INET)
+                       iafamily = AF_INET6;
 
-               if (x->props.family == AF_INET) {
-                       x->inner_mode = inner_mode;
-                       x->inner_mode_iaf = inner_mode_iaf;
-               } else {
-                       x->inner_mode = inner_mode_iaf;
-                       x->inner_mode_iaf = inner_mode;
+               inner_mode_iaf = xfrm_get_mode(x->props.mode, iafamily);
+               if (inner_mode_iaf) {
+                       if (inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)
+                               x->inner_mode_iaf = inner_mode_iaf;
+                       else
+                               xfrm_put_mode(inner_mode_iaf);
                }
        }