vsprintf: move local vars to block local vars and remove unneeded ones
[safe/jmp/linux-2.6] / net / xfrm / xfrm_state.c
index 21db37a..d847f1a 100644 (file)
 #include <linux/cache.h>
 #include <linux/audit.h>
 #include <asm/uaccess.h>
+#include <linux/ktime.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
 
 #include "xfrm_hash.h"
 
-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)
@@ -360,7 +355,7 @@ static void xfrm_put_mode(struct xfrm_mode *mode)
 
 static void xfrm_state_gc_destroy(struct xfrm_state *x)
 {
-       del_timer_sync(&x->timer);
+       tasklet_hrtimer_cancel(&x->mtimer);
        del_timer_sync(&x->rtimer);
        kfree(x->aalg);
        kfree(x->ealg);
@@ -406,9 +401,10 @@ static inline unsigned long make_jiffies(long secs)
                return secs*HZ;
 }
 
-static void xfrm_timer_handler(unsigned long data)
+static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me)
 {
-       struct xfrm_state *x = (struct xfrm_state*)data;
+       struct tasklet_hrtimer *thr = container_of(me, struct tasklet_hrtimer, timer);
+       struct xfrm_state *x = container_of(thr, struct xfrm_state, mtimer);
        struct net *net = xs_net(x);
        unsigned long now = get_seconds();
        long next = LONG_MAX;
@@ -459,8 +455,9 @@ static void xfrm_timer_handler(unsigned long data)
        if (warn)
                km_state_expired(x, 0, 0);
 resched:
-       if (next != LONG_MAX)
-               mod_timer(&x->timer, jiffies + make_jiffies(next));
+       if (next != LONG_MAX){
+               tasklet_hrtimer_start(&x->mtimer, ktime_set(next, 0), HRTIMER_MODE_REL);
+       }
 
        goto out;
 
@@ -482,6 +479,7 @@ expired:
 
 out:
        spin_unlock(&x->lock);
+       return HRTIMER_NORESTART;
 }
 
 static void xfrm_replay_timer_handler(unsigned long data);
@@ -500,7 +498,7 @@ struct xfrm_state *xfrm_state_alloc(struct net *net)
                INIT_HLIST_NODE(&x->bydst);
                INIT_HLIST_NODE(&x->bysrc);
                INIT_HLIST_NODE(&x->byspi);
-               setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
+               tasklet_hrtimer_init(&x->mtimer, xfrm_timer_handler, CLOCK_REALTIME, HRTIMER_MODE_ABS);
                setup_timer(&x->rtimer, xfrm_replay_timer_handler,
                                (unsigned long)x);
                x->curlft.add_time = get_seconds();
@@ -676,22 +674,10 @@ static struct xfrm_state *__xfrm_state_lookup(struct net *net, xfrm_address_t *d
        hlist_for_each_entry(x, entry, net->xfrm.state_byspi+h, byspi) {
                if (x->props.family != family ||
                    x->id.spi       != spi ||
-                   x->id.proto     != proto)
+                   x->id.proto     != proto ||
+                   xfrm_addr_cmp(&x->id.daddr, daddr, family))
                        continue;
 
-               switch (family) {
-               case AF_INET:
-                       if (x->id.daddr.a4 != daddr->a4)
-                               continue;
-                       break;
-               case AF_INET6:
-                       if (!ipv6_addr_equal((struct in6_addr *)daddr,
-                                            (struct in6_addr *)
-                                            x->id.daddr.a6))
-                               continue;
-                       break;
-               }
-
                xfrm_state_hold(x);
                return x;
        }
@@ -707,26 +693,11 @@ static struct xfrm_state *__xfrm_state_lookup_byaddr(struct net *net, xfrm_addre
 
        hlist_for_each_entry(x, entry, net->xfrm.state_bysrc+h, bysrc) {
                if (x->props.family != family ||
-                   x->id.proto     != proto)
+                   x->id.proto     != proto ||
+                   xfrm_addr_cmp(&x->id.daddr, daddr, family) ||
+                   xfrm_addr_cmp(&x->props.saddr, saddr, family))
                        continue;
 
-               switch (family) {
-               case AF_INET:
-                       if (x->id.daddr.a4 != daddr->a4 ||
-                           x->props.saddr.a4 != saddr->a4)
-                               continue;
-                       break;
-               case AF_INET6:
-                       if (!ipv6_addr_equal((struct in6_addr *)daddr,
-                                            (struct in6_addr *)
-                                            x->id.daddr.a6) ||
-                           !ipv6_addr_equal((struct in6_addr *)saddr,
-                                            (struct in6_addr *)
-                                            x->props.saddr.a6))
-                               continue;
-                       break;
-               }
-
                xfrm_state_hold(x);
                return x;
        }
@@ -756,14 +727,53 @@ 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;
+       unsigned int h, h_wildcard;
        struct hlist_node *entry;
        struct xfrm_state *x, *x0, *to_put;
        int acquire_in_progress = 0;
@@ -781,40 +791,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_wildcard = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, family);
+       hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h_wildcard, 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 &&
@@ -851,9 +848,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;
-                       add_timer(&x->timer);
+                       x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
+                       tasklet_hrtimer_start(&x->mtimer, ktime_set(net->xfrm.sysctl_acq_expires, 0), HRTIMER_MODE_REL);
                        net->xfrm.state_num++;
                        xfrm_hash_grow_check(net, x->bydst.next != NULL);
                } else {
@@ -930,7 +926,7 @@ static void __xfrm_state_insert(struct xfrm_state *x)
                hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
        }
 
-       mod_timer(&x->timer, jiffies + HZ);
+       tasklet_hrtimer_start(&x->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL);
        if (x->replay_maxage)
                mod_timer(&x->rtimer, jiffies + x->replay_maxage);
 
@@ -983,25 +979,11 @@ static struct xfrm_state *__find_acq_core(struct net *net, unsigned short family
                    x->props.family != family ||
                    x->km.state     != XFRM_STATE_ACQ ||
                    x->id.spi       != 0 ||
-                   x->id.proto     != proto)
+                   x->id.proto     != proto ||
+                   xfrm_addr_cmp(&x->id.daddr, daddr, family) ||
+                   xfrm_addr_cmp(&x->props.saddr, saddr, family))
                        continue;
 
-               switch (family) {
-               case AF_INET:
-                       if (x->id.daddr.a4    != daddr->a4 ||
-                           x->props.saddr.a4 != saddr->a4)
-                               continue;
-                       break;
-               case AF_INET6:
-                       if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6,
-                                            (struct in6_addr *)daddr) ||
-                           !ipv6_addr_equal((struct in6_addr *)
-                                            x->props.saddr.a6,
-                                            (struct in6_addr *)saddr))
-                               continue;
-                       break;
-               }
-
                xfrm_state_hold(x);
                return x;
        }
@@ -1040,10 +1022,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;
-               add_timer(&x->timer);
+               tasklet_hrtimer_start(&x->mtimer, ktime_set(net->xfrm.sysctl_acq_expires, 0), HRTIMER_MODE_REL);
                list_add(&x->km.all, &net->xfrm.state_all);
                hlist_add_head(&x->bydst, net->xfrm.state_bydst+h);
                h = xfrm_src_hash(net, daddr, saddr, family);
@@ -1133,7 +1114,7 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
        x->props.saddr = orig->props.saddr;
 
        if (orig->aalg) {
-               x->aalg = xfrm_algo_clone(orig->aalg);
+               x->aalg = xfrm_algo_auth_clone(orig->aalg);
                if (!x->aalg)
                        goto error;
        }
@@ -1323,7 +1304,7 @@ out:
                memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
                x1->km.dying = 0;
 
-               mod_timer(&x1->timer, jiffies + HZ);
+               tasklet_hrtimer_start(&x1->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL);
                if (x1->curlft.use_time)
                        xfrm_state_check_expire(x1);
 
@@ -1348,7 +1329,7 @@ int xfrm_state_check_expire(struct xfrm_state *x)
        if (x->curlft.bytes >= x->lft.hard_byte_limit ||
            x->curlft.packets >= x->lft.hard_packet_limit) {
                x->km.state = XFRM_STATE_EXPIRED;
-               mod_timer(&x->timer, jiffies);
+               tasklet_hrtimer_start(&x->mtimer, ktime_set(0,0), HRTIMER_MODE_REL);
                return -EINVAL;
        }
 
@@ -1597,7 +1578,7 @@ void xfrm_state_walk_done(struct xfrm_state_walk *walk)
 
        spin_lock_bh(&xfrm_state_lock);
        list_del(&walk->all);
-       spin_lock_bh(&xfrm_state_lock);
+       spin_unlock_bh(&xfrm_state_lock);
 }
 EXPORT_SYMBOL(xfrm_state_walk_done);
 
@@ -1833,7 +1814,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;
@@ -1842,7 +1823,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;
                }
@@ -2030,8 +2011,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;
 
@@ -2039,22 +2021,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);
                }
        }
 
@@ -2114,8 +2091,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);