We already support setting an output mark at the xfrm_state,
unfortunately this does not support the input direction and
masking the marks that will be applied to the skb. This change
adds support applying a masked value in both directions.

The existing XFRMA_OUTPUT_MARK number is reused for this purpose
and as it is now bi-directional, it is renamed to XFRMA_SET_MARK.

An additional XFRMA_SET_MARK_MASK attribute is added for setting the
mask. If the attribute mask not provided, it is set to 0xffffffff,
keeping the XFRMA_OUTPUT_MARK existing 'full mask' semantics.

Co-developed-by: Tobias Brunner <tob...@strongswan.org>
Co-developed-by: Eyal Birger <eyal.bir...@gmail.com>
Co-developed-by: Lorenzo Colitti <lore...@google.com>
Signed-off-by: Steffen Klassert <steffen.klass...@secunet.com>
Signed-off-by: Tobias Brunner <tob...@strongswan.org>
Signed-off-by: Eyal Birger <eyal.bir...@gmail.com>
Signed-off-by: Lorenzo Colitti <lore...@google.com>
---
 include/net/xfrm.h        |  9 ++++++++-
 include/uapi/linux/xfrm.h |  4 +++-
 net/xfrm/xfrm_device.c    |  3 ++-
 net/xfrm/xfrm_input.c     |  2 ++
 net/xfrm/xfrm_output.c    |  3 +--
 net/xfrm/xfrm_policy.c    |  5 +++--
 net/xfrm/xfrm_user.c      | 48 +++++++++++++++++++++++++++++++++++++----------
 7 files changed, 57 insertions(+), 17 deletions(-)

diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 557122846e0e..3dc83ba26f62 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -166,7 +166,7 @@ struct xfrm_state {
                int             header_len;
                int             trailer_len;
                u32             extra_flags;
-               u32             output_mark;
+               struct xfrm_mark        smark;
        } props;
 
        struct xfrm_lifetime_cfg lft;
@@ -2012,6 +2012,13 @@ static inline int xfrm_mark_put(struct sk_buff *skb, 
const struct xfrm_mark *m)
        return ret;
 }
 
+static inline __u32 xfrm_smark_get(__u32 mark, struct xfrm_state *x)
+{
+       struct xfrm_mark *m = &x->props.smark;
+
+       return (m->v & m->m) | (mark & ~m->m);
+}
+
 static inline int xfrm_tunnel_check(struct sk_buff *skb, struct xfrm_state *x,
                                    unsigned int family)
 {
diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h
index e3af2859188b..5a6ed7ce5a29 100644
--- a/include/uapi/linux/xfrm.h
+++ b/include/uapi/linux/xfrm.h
@@ -305,9 +305,11 @@ enum xfrm_attr_type_t {
        XFRMA_ADDRESS_FILTER,   /* struct xfrm_address_filter */
        XFRMA_PAD,
        XFRMA_OFFLOAD_DEV,      /* struct xfrm_state_offload */
-       XFRMA_OUTPUT_MARK,      /* __u32 */
+       XFRMA_SET_MARK,         /* __u32 */
+       XFRMA_SET_MARK_MASK,    /* __u32 */
        __XFRMA_MAX
 
+#define XFRMA_OUTPUT_MARK XFRMA_SET_MARK       /* Compatibility */
 #define XFRMA_MAX (__XFRMA_MAX - 1)
 };
 
diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
index 175941e15a6e..16c1230d20fa 100644
--- a/net/xfrm/xfrm_device.c
+++ b/net/xfrm/xfrm_device.c
@@ -162,7 +162,8 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state 
*x,
                }
 
                dst = __xfrm_dst_lookup(net, 0, 0, saddr, daddr,
-                                       x->props.family, x->props.output_mark);
+                                       x->props.family,
+                                       xfrm_smark_get(0, x));
                if (IS_ERR(dst))
                        return 0;
 
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 352abca2605f..074810436242 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -339,6 +339,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 
spi, int encap_type)
                        goto drop;
                }
 
+               skb->mark = xfrm_smark_get(skb->mark, x);
+
                skb->sp->xvec[skb->sp->len++] = x;
 
 lock:
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index 89b178a78dc7..45ba07ab3e4f 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -66,8 +66,7 @@ static int xfrm_output_one(struct sk_buff *skb, int err)
                        goto error_nolock;
                }
 
-               if (x->props.output_mark)
-                       skb->mark = x->props.output_mark;
+               skb->mark = xfrm_smark_get(skb->mark, x);
 
                err = x->outer_mode->output(x, skb);
                if (err) {
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 5f48251c1319..7637637717ec 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1607,10 +1607,11 @@ static struct dst_entry *xfrm_bundle_create(struct 
xfrm_policy *policy,
                dst_copy_metrics(dst1, dst);
 
                if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
+                       __u32 mark = xfrm_smark_get(fl->flowi_mark, xfrm[i]);
+
                        family = xfrm[i]->props.family;
                        dst = xfrm_dst_lookup(xfrm[i], tos, fl->flowi_oif,
-                                             &saddr, &daddr, family,
-                                             xfrm[i]->props.output_mark);
+                                             &saddr, &daddr, family, mark);
                        err = PTR_ERR(dst);
                        if (IS_ERR(dst))
                                goto put_states;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 080035f056d9..9602cc9e05ab 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -527,6 +527,19 @@ static void xfrm_update_ae_params(struct xfrm_state *x, 
struct nlattr **attrs,
                x->replay_maxdiff = nla_get_u32(rt);
 }
 
+static void xfrm_smark_init(struct nlattr **attrs, struct xfrm_mark *m)
+{
+       if (attrs[XFRMA_SET_MARK]) {
+               m->v = nla_get_u32(attrs[XFRMA_SET_MARK]);
+               if (attrs[XFRMA_SET_MARK_MASK])
+                       m->m = nla_get_u32(attrs[XFRMA_SET_MARK_MASK]);
+               else
+                       m->m = 0xffffffff;
+       } else {
+               m->v = m->m = 0;
+       }
+}
+
 static struct xfrm_state *xfrm_state_construct(struct net *net,
                                               struct xfrm_usersa_info *p,
                                               struct nlattr **attrs,
@@ -579,8 +592,7 @@ static struct xfrm_state *xfrm_state_construct(struct net 
*net,
 
        xfrm_mark_get(attrs, &x->mark);
 
-       if (attrs[XFRMA_OUTPUT_MARK])
-               x->props.output_mark = nla_get_u32(attrs[XFRMA_OUTPUT_MARK]);
+       xfrm_smark_init(attrs, &x->props.smark);
 
        err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV]);
        if (err)
@@ -824,6 +836,18 @@ static int copy_to_user_auth(struct xfrm_algo_auth *auth, 
struct sk_buff *skb)
        return 0;
 }
 
+static int xfrm_smark_put(struct sk_buff *skb, struct xfrm_mark *m)
+{
+       int ret = 0;
+
+       if (m->v | m->m) {
+               ret = nla_put_u32(skb, XFRMA_SET_MARK, m->v);
+               if (!ret)
+                       ret = nla_put_u32(skb, XFRMA_SET_MARK_MASK, m->m);
+       }
+       return ret;
+}
+
 /* Don't change this without updating xfrm_sa_len! */
 static int copy_to_user_state_extra(struct xfrm_state *x,
                                    struct xfrm_usersa_info *p,
@@ -887,6 +911,11 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
        ret = xfrm_mark_put(skb, &x->mark);
        if (ret)
                goto out;
+
+       ret = xfrm_smark_put(skb, &x->props.smark);
+       if (ret)
+               goto out;
+
        if (x->replay_esn)
                ret = nla_put(skb, XFRMA_REPLAY_ESN_VAL,
                              xfrm_replay_state_esn_len(x->replay_esn),
@@ -900,11 +929,7 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
                ret = copy_user_offload(&x->xso, skb);
        if (ret)
                goto out;
-       if (x->props.output_mark) {
-               ret = nla_put_u32(skb, XFRMA_OUTPUT_MARK, x->props.output_mark);
-               if (ret)
-                       goto out;
-       }
+
        if (x->security)
                ret = copy_sec_ctx(x->security, skb);
 out:
@@ -2493,7 +2518,8 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] 
= {
        [XFRMA_PROTO]           = { .type = NLA_U8 },
        [XFRMA_ADDRESS_FILTER]  = { .len = sizeof(struct xfrm_address_filter) },
        [XFRMA_OFFLOAD_DEV]     = { .len = sizeof(struct xfrm_user_offload) },
-       [XFRMA_OUTPUT_MARK]     = { .type = NLA_U32 },
+       [XFRMA_SET_MARK]        = { .type = NLA_U32 },
+       [XFRMA_SET_MARK_MASK]   = { .type = NLA_U32 },
 };
 
 static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = {
@@ -2719,8 +2745,10 @@ static inline unsigned int xfrm_sa_len(struct xfrm_state 
*x)
                l += nla_total_size(sizeof(x->props.extra_flags));
        if (x->xso.dev)
                 l += nla_total_size(sizeof(x->xso));
-       if (x->props.output_mark)
-               l += nla_total_size(sizeof(x->props.output_mark));
+       if (x->props.smark.v | x->props.smark.m) {
+               l += nla_total_size(sizeof(x->props.smark.v));
+               l += nla_total_size(sizeof(x->props.smark.m));
+       }
 
        /* Must count x->lastused as it may become non-zero behind our back. */
        l += nla_total_size_64bit(sizeof(u64));
-- 
2.14.1

Reply via email to