xfrm: Allow user space config of SAD mark
Jamal Hadi Salim [Mon, 22 Feb 2010 11:32:59 +0000 (11:32 +0000)]
Add ability for netlink userspace to manipulate the SAD
and manipulate the mark, retrieve it and get events with a defined
mark.
MIGRATE may be added later.

Signed-off-by: Jamal Hadi Salim <hadi@cyberus.ca>
Signed-off-by: David S. Miller <davem@davemloft.net>

net/xfrm/xfrm_user.c

index 02a67b4..303092f 100644 (file)
@@ -32,7 +32,6 @@
 #endif
 
 #define DUMMY_MARK 0
-static struct xfrm_mark dummy_mark = {0, 0};
 
 static inline int aead_len(struct xfrm_algo_aead *alg)
 {
@@ -449,6 +448,8 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
                        goto error;
        }
 
+       xfrm_mark_get(attrs, &x->mark);
+
        err = xfrm_init_state(x);
        if (err)
                goto error;
@@ -529,11 +530,13 @@ static struct xfrm_state *xfrm_user_state_lookup(struct net *net,
                                                 int *errp)
 {
        struct xfrm_state *x = NULL;
+       struct xfrm_mark m;
        int err;
+       u32 mark = xfrm_mark_get(attrs, &m);
 
        if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) {
                err = -ESRCH;
-               x = xfrm_state_lookup(net, DUMMY_MARK, &p->daddr, p->spi, p->proto, p->family);
+               x = xfrm_state_lookup(net, mark, &p->daddr, p->spi, p->proto, p->family);
        } else {
                xfrm_address_t *saddr = NULL;
 
@@ -544,7 +547,8 @@ static struct xfrm_state *xfrm_user_state_lookup(struct net *net,
                }
 
                err = -ESRCH;
-               x = xfrm_state_lookup_byaddr(net, DUMMY_MARK, &p->daddr, saddr,
+               x = xfrm_state_lookup_byaddr(net, mark,
+                                            &p->daddr, saddr,
                                             p->proto, p->family);
        }
 
@@ -686,6 +690,9 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
        if (x->encap)
                NLA_PUT(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap);
 
+       if (xfrm_mark_put(skb, &x->mark))
+               goto nla_put_failure;
+
        if (x->security && copy_sec_ctx(x->security, skb) < 0)
                goto nla_put_failure;
 
@@ -950,6 +957,8 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
        xfrm_address_t *daddr;
        int family;
        int err;
+       u32 mark;
+       struct xfrm_mark m;
 
        p = nlmsg_data(nlh);
        err = verify_userspi_info(p);
@@ -960,8 +969,10 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
        daddr = &p->info.id.daddr;
 
        x = NULL;
+
+       mark = xfrm_mark_get(attrs, &m);
        if (p->info.seq) {
-               x = xfrm_find_acq_byseq(net, DUMMY_MARK, p->info.seq);
+               x = xfrm_find_acq_byseq(net, mark, p->info.seq);
                if (x && xfrm_addr_cmp(&x->id.daddr, daddr, family)) {
                        xfrm_state_put(x);
                        x = NULL;
@@ -969,7 +980,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
        }
 
        if (!x)
-               x = xfrm_find_acq(net, &dummy_mark, p->info.mode, p->info.reqid,
+               x = xfrm_find_acq(net, &m, p->info.mode, p->info.reqid,
                                  p->info.id.proto, daddr,
                                  &p->info.saddr, 1,
                                  family);
@@ -1474,8 +1485,8 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
                        if (err)
                                return err;
                }
-               xp = xfrm_policy_bysel_ctx(net, DUMMY_MARK, type, p->dir,
-                                          &p->sel, ctx, delete, &err);
+               xp = xfrm_policy_bysel_ctx(net, DUMMY_MARK, type, p->dir, &p->sel,
+                                          ctx, delete, &err);
                security_xfrm_policy_free(ctx);
        }
        if (xp == NULL)
@@ -1547,6 +1558,7 @@ static inline size_t xfrm_aevent_msgsize(void)
        return NLMSG_ALIGN(sizeof(struct xfrm_aevent_id))
               + nla_total_size(sizeof(struct xfrm_replay_state))
               + nla_total_size(sizeof(struct xfrm_lifetime_cur))
+              + nla_total_size(sizeof(struct xfrm_mark))
               + nla_total_size(4) /* XFRM_AE_RTHR */
               + nla_total_size(4); /* XFRM_AE_ETHR */
 }
@@ -1579,6 +1591,9 @@ static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, struct km_eve
                NLA_PUT_U32(skb, XFRMA_ETIMER_THRESH,
                            x->replay_maxage * 10 / HZ);
 
+       if (xfrm_mark_put(skb, &x->mark))
+               goto nla_put_failure;
+
        return nlmsg_end(skb, nlh);
 
 nla_put_failure:
@@ -1594,6 +1609,8 @@ static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct sk_buff *r_skb;
        int err;
        struct km_event c;
+       u32 mark;
+       struct xfrm_mark m;
        struct xfrm_aevent_id *p = nlmsg_data(nlh);
        struct xfrm_usersa_id *id = &p->sa_id;
 
@@ -1601,7 +1618,9 @@ static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
        if (r_skb == NULL)
                return -ENOMEM;
 
-       x = xfrm_state_lookup(net, DUMMY_MARK, &id->daddr, id->spi, id->proto, id->family);
+       mark = xfrm_mark_get(attrs, &m);
+
+       x = xfrm_state_lookup(net, mark, &id->daddr, id->spi, id->proto, id->family);
        if (x == NULL) {
                kfree_skb(r_skb);
                return -ESRCH;
@@ -1632,6 +1651,8 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct xfrm_state *x;
        struct km_event c;
        int err = - EINVAL;
+       u32 mark = 0;
+       struct xfrm_mark m;
        struct xfrm_aevent_id *p = nlmsg_data(nlh);
        struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
        struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
@@ -1643,7 +1664,9 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
        if (!(nlh->nlmsg_flags&NLM_F_REPLACE))
                return err;
 
-       x = xfrm_state_lookup(net, DUMMY_MARK, &p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family);
+       mark = xfrm_mark_get(attrs, &m);
+
+       x = xfrm_state_lookup(net, mark, &p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family);
        if (x == NULL)
                return -ESRCH;
 
@@ -1729,7 +1752,8 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
                        if (err)
                                return err;
                }
-               xp = xfrm_policy_bysel_ctx(net, DUMMY_MARK, type, p->dir, &p->sel, ctx, 0, &err);
+               xp = xfrm_policy_bysel_ctx(net, DUMMY_MARK, type, p->dir,
+                                          &p->sel, ctx, 0, &err);
                security_xfrm_policy_free(ctx);
        }
        if (xp == NULL)
@@ -1769,8 +1793,10 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
        int err;
        struct xfrm_user_expire *ue = nlmsg_data(nlh);
        struct xfrm_usersa_info *p = &ue->state;
+       struct xfrm_mark m;
+       u32 mark = xfrm_mark_get(attrs, &m);;
 
-       x = xfrm_state_lookup(net, DUMMY_MARK, &p->id.daddr, p->id.spi, p->id.proto, p->family);
+       x = xfrm_state_lookup(net, mark, &p->id.daddr, p->id.spi, p->id.proto, p->family);
 
        err = -ENOENT;
        if (x == NULL)
@@ -1804,6 +1830,7 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct xfrm_user_tmpl *ut;
        int i;
        struct nlattr *rt = attrs[XFRMA_TMPL];
+       struct xfrm_mark mark;
 
        struct xfrm_user_acquire *ua = nlmsg_data(nlh);
        struct xfrm_state *x = xfrm_state_alloc(net);
@@ -1812,6 +1839,8 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
        if (!x)
                goto nomem;
 
+       xfrm_mark_get(attrs, &mark);
+
        err = verify_newpolicy_info(&ua->policy);
        if (err)
                goto bad_policy;
@@ -1824,7 +1853,8 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
        memcpy(&x->id, &ua->id, sizeof(ua->id));
        memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr));
        memcpy(&x->sel, &ua->sel, sizeof(ua->sel));
-
+       xp->mark.m = x->mark.m = mark.m;
+       xp->mark.v = x->mark.v = mark.v;
        ut = nla_data(rt);
        /* extract the templates and for each call km_key */
        for (i = 0; i < xp->xfrm_nr; i++, ut++) {
@@ -2084,6 +2114,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
        [XFRMA_POLICY_TYPE]     = { .len = sizeof(struct xfrm_userpolicy_type)},
        [XFRMA_MIGRATE]         = { .len = sizeof(struct xfrm_user_migrate) },
        [XFRMA_KMADDRESS]       = { .len = sizeof(struct xfrm_user_kmaddress) },
+       [XFRMA_MARK]            = { .len = sizeof(struct xfrm_mark) },
 };
 
 static struct xfrm_link {
@@ -2163,7 +2194,8 @@ static void xfrm_netlink_rcv(struct sk_buff *skb)
 
 static inline size_t xfrm_expire_msgsize(void)
 {
-       return NLMSG_ALIGN(sizeof(struct xfrm_user_expire));
+       return NLMSG_ALIGN(sizeof(struct xfrm_user_expire))
+              + nla_total_size(sizeof(struct xfrm_mark));
 }
 
 static int build_expire(struct sk_buff *skb, struct xfrm_state *x, struct km_event *c)
@@ -2179,7 +2211,13 @@ static int build_expire(struct sk_buff *skb, struct xfrm_state *x, struct km_eve
        copy_to_user_state(x, &ue->state);
        ue->hard = (c->data.hard != 0) ? 1 : 0;
 
+       if (xfrm_mark_put(skb, &x->mark))
+               goto nla_put_failure;
+
        return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+       return -EMSGSIZE;
 }
 
 static int xfrm_exp_state_notify(struct xfrm_state *x, struct km_event *c)
@@ -2191,8 +2229,10 @@ static int xfrm_exp_state_notify(struct xfrm_state *x, struct km_event *c)
        if (skb == NULL)
                return -ENOMEM;
 
-       if (build_expire(skb, x, c) < 0)
-               BUG();
+       if (build_expire(skb, x, c) < 0) {
+               kfree_skb(skb);
+               return -EMSGSIZE;
+       }
 
        return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC);
 }
@@ -2280,6 +2320,7 @@ static int xfrm_notify_sa(struct xfrm_state *x, struct km_event *c)
        if (c->event == XFRM_MSG_DELSA) {
                len += nla_total_size(headlen);
                headlen = sizeof(*id);
+               len += nla_total_size(sizeof(struct xfrm_mark));
        }
        len += NLMSG_ALIGN(headlen);
 
@@ -2350,6 +2391,7 @@ static inline size_t xfrm_acquire_msgsize(struct xfrm_state *x,
 {
        return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire))
               + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
+              + nla_total_size(sizeof(struct xfrm_mark))
               + nla_total_size(xfrm_user_sec_ctx_size(x->security))
               + userpolicy_type_attrsize();
 }