]> 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 0d974fc9dd6c3641ccd270aa024e9e1f781fa7a6..62a5425cc6aa1c83a386f2123281d4ccc07c9cdc 100644 (file)
 
 #include "xfrm_hash.h"
 
-struct sock *xfrm_nl;
-EXPORT_SYMBOL(xfrm_nl);
-
-u32 sysctl_xfrm_aevent_etime __read_mostly = XFRM_AE_ETIME;
-EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
-
-u32 sysctl_xfrm_aevent_rseqth __read_mostly = XFRM_AE_SEQT_SIZE;
-EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
-
-u32 sysctl_xfrm_acq_expires __read_mostly = 30;
-
 /* Each xfrm_state may be linked to two tables:
 
    1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
@@ -759,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;
@@ -784,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 &&
@@ -854,8 +869,8 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
                                h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, family);
                                hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
                        }
-                       x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
-                       x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
+                       x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
+                       x->timer.expires = jiffies + net->xfrm.sysctl_acq_expires*HZ;
                        add_timer(&x->timer);
                        net->xfrm.state_num++;
                        xfrm_hash_grow_check(net, x->bydst.next != NULL);
@@ -1043,9 +1058,9 @@ static struct xfrm_state *__find_acq_core(struct net *net, unsigned short family
                x->props.family = family;
                x->props.mode = mode;
                x->props.reqid = reqid;
-               x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
+               x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
                xfrm_state_hold(x);
-               x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
+               x->timer.expires = jiffies + net->xfrm.sysctl_acq_expires*HZ;
                add_timer(&x->timer);
                list_add(&x->km.all, &net->xfrm.state_all);
                hlist_add_head(&x->bydst, net->xfrm.state_bydst+h);
@@ -1544,7 +1559,7 @@ unlock:
 }
 EXPORT_SYMBOL(xfrm_alloc_spi);
 
-int xfrm_state_walk(struct xfrm_state_walk *walk,
+int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
                    int (*func)(struct xfrm_state *, int, void*),
                    void *data)
 {
@@ -1557,10 +1572,10 @@ int xfrm_state_walk(struct xfrm_state_walk *walk,
 
        spin_lock_bh(&xfrm_state_lock);
        if (list_empty(&walk->all))
-               x = list_first_entry(&init_net.xfrm.state_all, struct xfrm_state_walk, all);
+               x = list_first_entry(&net->xfrm.state_all, struct xfrm_state_walk, all);
        else
                x = list_entry(&walk->all, struct xfrm_state_walk, all);
-       list_for_each_entry_from(x, &init_net.xfrm.state_all, all) {
+       list_for_each_entry_from(x, &net->xfrm.state_all, all) {
                if (x->state == XFRM_STATE_DEAD)
                        continue;
                state = container_of(x, struct xfrm_state, km);
@@ -1659,7 +1674,7 @@ static void xfrm_replay_timer_handler(unsigned long data)
        spin_lock(&x->lock);
 
        if (x->km.state == XFRM_STATE_VALID) {
-               if (xfrm_aevent_is_on())
+               if (xfrm_aevent_is_on(xs_net(x)))
                        xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
                else
                        x->xflags |= XFRM_TIME_DEFER;
@@ -1715,7 +1730,7 @@ void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
                x->replay.bitmap |= (1U << diff);
        }
 
-       if (xfrm_aevent_is_on())
+       if (xfrm_aevent_is_on(xs_net(x)))
                xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
 }
 
@@ -1836,7 +1851,7 @@ int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
 EXPORT_SYMBOL(km_migrate);
 #endif
 
-int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
+int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
 {
        int err = -EINVAL;
        int ret;
@@ -1845,7 +1860,7 @@ int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
        read_lock(&xfrm_km_lock);
        list_for_each_entry(km, &xfrm_km_list, list) {
                if (km->report) {
-                       ret = km->report(proto, sel, addr);
+                       ret = km->report(net, proto, sel, addr);
                        if (!ret)
                                err = ret;
                }
@@ -2033,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;
 
@@ -2042,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);
                }
        }
 
@@ -2117,8 +2128,16 @@ out_bydst:
 
 void xfrm_state_fini(struct net *net)
 {
+       struct xfrm_audit audit_info;
        unsigned int sz;
 
+       flush_work(&net->xfrm.state_hash_work);
+       audit_info.loginuid = -1;
+       audit_info.sessionid = -1;
+       audit_info.secid = 0;
+       xfrm_state_flush(net, IPSEC_PROTO_ANY, &audit_info);
+       flush_work(&net->xfrm.state_gc_work);
+
        WARN_ON(!list_empty(&net->xfrm.state_all));
 
        sz = (net->xfrm.state_hmask + 1) * sizeof(struct hlist_head);