]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/xfrm/xfrm_state.c
Merge branch 'timers/urgent' of ssh://master.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-omap-h63xx.git] / net / xfrm / xfrm_state.c
index 7ba65e82941c4f7794f6ada15a418d60606dbba4..72fddafd891a0b3f3be603804e6c5727a56a5b3d 100644 (file)
@@ -50,6 +50,7 @@ static DEFINE_SPINLOCK(xfrm_state_lock);
  * Main use is finding SA after policy selected tunnel or transport mode.
  * Also, it can be used by ah/esp icmp error handler to find offending SA.
  */
+static LIST_HEAD(xfrm_state_all);
 static struct hlist_head *xfrm_state_bydst __read_mostly;
 static struct hlist_head *xfrm_state_bysrc __read_mostly;
 static struct hlist_head *xfrm_state_byspi __read_mostly;
@@ -388,6 +389,8 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
        kfree(x->coaddr);
        if (x->inner_mode)
                xfrm_put_mode(x->inner_mode);
+       if (x->inner_mode_iaf)
+               xfrm_put_mode(x->inner_mode_iaf);
        if (x->outer_mode)
                xfrm_put_mode(x->outer_mode);
        if (x->type) {
@@ -493,7 +496,8 @@ expired:
                km_state_expired(x, 1, 0);
 
        xfrm_audit_state_delete(x, err ? 0 : 1,
-                               audit_get_loginuid(current), 0);
+                               audit_get_loginuid(current),
+                               audit_get_sessionid(current), 0);
 
 out:
        spin_unlock(&x->lock);
@@ -510,6 +514,7 @@ struct xfrm_state *xfrm_state_alloc(void)
        if (x) {
                atomic_set(&x->refcnt, 1);
                atomic_set(&x->tunnel_users, 0);
+               INIT_LIST_HEAD(&x->all);
                INIT_HLIST_NODE(&x->bydst);
                INIT_HLIST_NODE(&x->bysrc);
                INIT_HLIST_NODE(&x->byspi);
@@ -523,6 +528,8 @@ struct xfrm_state *xfrm_state_alloc(void)
                x->lft.hard_packet_limit = XFRM_INF;
                x->replay_maxage = 0;
                x->replay_maxdiff = 0;
+               x->inner_mode = NULL;
+               x->inner_mode_iaf = NULL;
                spin_lock_init(&x->lock);
        }
        return x;
@@ -533,6 +540,10 @@ void __xfrm_state_destroy(struct xfrm_state *x)
 {
        BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
 
+       spin_lock_bh(&xfrm_state_lock);
+       list_del(&x->all);
+       spin_unlock_bh(&xfrm_state_lock);
+
        spin_lock_bh(&xfrm_state_gc_lock);
        hlist_add_head(&x->bydst, &xfrm_state_gc_list);
        spin_unlock_bh(&xfrm_state_gc_lock);
@@ -593,6 +604,7 @@ xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
                           (err = security_xfrm_state_delete(x)) != 0) {
                                xfrm_audit_state_delete(x, 0,
                                                        audit_info->loginuid,
+                                                       audit_info->sessionid,
                                                        audit_info->secid);
                                return err;
                        }
@@ -631,6 +643,7 @@ restart:
                                err = xfrm_state_delete(x);
                                xfrm_audit_state_delete(x, err ? 0 : 1,
                                                        audit_info->loginuid,
+                                                       audit_info->sessionid,
                                                        audit_info->secid);
                                xfrm_state_put(x);
 
@@ -796,7 +809,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
                              selector.
                         */
                        if (x->km.state == XFRM_STATE_VALID) {
-                               if (!xfrm_selector_match(&x->sel, fl, x->sel.family) ||
+                               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 ||
@@ -909,6 +922,8 @@ static void __xfrm_state_insert(struct xfrm_state *x)
 
        x->genid = ++xfrm_state_genid;
 
+       list_add_tail(&x->all, &xfrm_state_all);
+
        h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
                          x->props.reqid, x->props.family);
        hlist_add_head(&x->bydst, xfrm_state_bydst+h);
@@ -1518,36 +1533,47 @@ unlock:
 }
 EXPORT_SYMBOL(xfrm_alloc_spi);
 
-int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
+int xfrm_state_walk(struct xfrm_state_walk *walk,
+                   int (*func)(struct xfrm_state *, int, void*),
                    void *data)
 {
-       int i;
-       struct xfrm_state *x, *last = NULL;
-       struct hlist_node *entry;
-       int count = 0;
+       struct xfrm_state *old, *x, *last = NULL;
        int err = 0;
 
+       if (walk->state == NULL && walk->count != 0)
+               return 0;
+
+       old = x = walk->state;
+       walk->state = NULL;
        spin_lock_bh(&xfrm_state_lock);
-       for (i = 0; i <= xfrm_state_hmask; i++) {
-               hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
-                       if (!xfrm_id_proto_match(x->id.proto, proto))
-                               continue;
-                       if (last) {
-                               err = func(last, count, data);
-                               if (err)
-                                       goto out;
+       if (x == NULL)
+               x = list_first_entry(&xfrm_state_all, struct xfrm_state, all);
+       list_for_each_entry_from(x, &xfrm_state_all, all) {
+               if (x->km.state == XFRM_STATE_DEAD)
+                       continue;
+               if (!xfrm_id_proto_match(x->id.proto, walk->proto))
+                       continue;
+               if (last) {
+                       err = func(last, walk->count, data);
+                       if (err) {
+                               xfrm_state_hold(last);
+                               walk->state = last;
+                               goto out;
                        }
-                       last = x;
-                       count++;
                }
+               last = x;
+               walk->count++;
        }
-       if (count == 0) {
+       if (walk->count == 0) {
                err = -ENOENT;
                goto out;
        }
-       err = func(last, 0, data);
+       if (last)
+               err = func(last, 0, data);
 out:
        spin_unlock_bh(&xfrm_state_lock);
+       if (old != NULL)
+               xfrm_state_put(old);
        return err;
 }
 EXPORT_SYMBOL(xfrm_state_walk);
@@ -1944,6 +1970,7 @@ int xfrm_state_mtu(struct xfrm_state *x, int mtu)
 int xfrm_init_state(struct xfrm_state *x)
 {
        struct xfrm_state_afinfo *afinfo;
+       struct xfrm_mode *inner_mode;
        int family = x->props.family;
        int err;
 
@@ -1962,13 +1989,48 @@ int xfrm_init_state(struct xfrm_state *x)
                goto error;
 
        err = -EPROTONOSUPPORT;
-       x->inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
-       if (x->inner_mode == NULL)
-               goto error;
 
-       if (!(x->inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
-           family != x->sel.family)
-               goto error;
+       if (x->sel.family != AF_UNSPEC) {
+               inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
+               if (inner_mode == NULL)
+                       goto error;
+
+               if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
+                   family != x->sel.family) {
+                       xfrm_put_mode(inner_mode);
+                       goto error;
+               }
+
+               x->inner_mode = inner_mode;
+       } else {
+               struct xfrm_mode *inner_mode_iaf;
+
+               inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
+               if (inner_mode == NULL)
+                       goto error;
+
+               if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
+                       xfrm_put_mode(inner_mode);
+                       goto error;
+               }
+
+               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) {
+                       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;
+               }
+       }
 
        x->type = xfrm_get_type(x->id.proto, family);
        if (x->type == NULL)
@@ -2053,7 +2115,7 @@ static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
                iph6 = ipv6_hdr(skb);
                audit_log_format(audit_buf,
                                 " src=" NIP6_FMT " dst=" NIP6_FMT
-                                " flowlbl=0x%x%x%x",
+                                " flowlbl=0x%x%02x%02x",
                                 NIP6(iph6->saddr),
                                 NIP6(iph6->daddr),
                                 iph6->flow_lbl[0] & 0x0f,
@@ -2064,14 +2126,14 @@ static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
 }
 
 void xfrm_audit_state_add(struct xfrm_state *x, int result,
-                         u32 auid, u32 secid)
+                         uid_t auid, u32 sessionid, u32 secid)
 {
        struct audit_buffer *audit_buf;
 
        audit_buf = xfrm_audit_start("SAD-add");
        if (audit_buf == NULL)
                return;
-       xfrm_audit_helper_usrinfo(auid, secid, audit_buf);
+       xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
        xfrm_audit_helper_sainfo(x, audit_buf);
        audit_log_format(audit_buf, " res=%u", result);
        audit_log_end(audit_buf);
@@ -2079,14 +2141,14 @@ void xfrm_audit_state_add(struct xfrm_state *x, int result,
 EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
 
 void xfrm_audit_state_delete(struct xfrm_state *x, int result,
-                            u32 auid, u32 secid)
+                            uid_t auid, u32 sessionid, u32 secid)
 {
        struct audit_buffer *audit_buf;
 
        audit_buf = xfrm_audit_start("SAD-delete");
        if (audit_buf == NULL)
                return;
-       xfrm_audit_helper_usrinfo(auid, secid, audit_buf);
+       xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
        xfrm_audit_helper_sainfo(x, audit_buf);
        audit_log_format(audit_buf, " res=%u", result);
        audit_log_end(audit_buf);