xfrm: Add a new lookup key to match xfrm interfaces.
authorSteffen Klassert <steffen.klassert@secunet.com>
Tue, 12 Jun 2018 12:07:07 +0000 (14:07 +0200)
committerSteffen Klassert <steffen.klassert@secunet.com>
Sat, 23 Jun 2018 14:07:15 +0000 (16:07 +0200)
This patch adds the xfrm interface id as a lookup key
for xfrm states and policies. With this we can assign
states and policies to virtual xfrm interfaces.

Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Acked-by: Shannon Nelson <shannon.nelson@oracle.com>
Acked-by: Benedict Wong <benedictwong@google.com>
Tested-by: Benedict Wong <benedictwong@google.com>
Tested-by: Antony Antony <antony@phenome.org>
Reviewed-by: Eyal Birger <eyal.birger@gmail.com>
include/net/xfrm.h
include/uapi/linux/xfrm.h
net/core/pktgen.c
net/key/af_key.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_state.c
net/xfrm/xfrm_user.c

index 3dc83ba26f6224bd8574978b9e2bb390e4324bca..e8bada4d2a455971176cc9567026b93913b0bcec 100644 (file)
@@ -147,6 +147,7 @@ struct xfrm_state {
        struct xfrm_id          id;
        struct xfrm_selector    sel;
        struct xfrm_mark        mark;
+       u32                     if_id;
        u32                     tfcpad;
 
        u32                     genid;
@@ -574,6 +575,7 @@ struct xfrm_policy {
        atomic_t                genid;
        u32                     priority;
        u32                     index;
+       u32                     if_id;
        struct xfrm_mark        mark;
        struct xfrm_selector    selector;
        struct xfrm_lifetime_cfg lft;
@@ -1533,7 +1535,7 @@ struct xfrm_state *xfrm_state_find(const xfrm_address_t *daddr,
                                   struct xfrm_tmpl *tmpl,
                                   struct xfrm_policy *pol, int *err,
                                   unsigned short family);
-struct xfrm_state *xfrm_stateonly_find(struct net *net, u32 mark,
+struct xfrm_state *xfrm_stateonly_find(struct net *net, u32 mark, u32 if_id,
                                       xfrm_address_t *daddr,
                                       xfrm_address_t *saddr,
                                       unsigned short family,
@@ -1690,20 +1692,20 @@ int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
                     void *);
 void xfrm_policy_walk_done(struct xfrm_policy_walk *walk, struct net *net);
 int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl);
-struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark,
+struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u32 if_id,
                                          u8 type, int dir,
                                          struct xfrm_selector *sel,
                                          struct xfrm_sec_ctx *ctx, int delete,
                                          int *err);
-struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir,
-                                    u32 id, int delete, int *err);
+struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u32 if_id, u8,
+                                    int dir, u32 id, int delete, int *err);
 int xfrm_policy_flush(struct net *net, u8 type, bool task_valid);
 void xfrm_policy_hash_rebuild(struct net *net);
 u32 xfrm_get_acqseq(void);
 int verify_spi_info(u8 proto, u32 min, u32 max);
 int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
 struct xfrm_state *xfrm_find_acq(struct net *net, const struct xfrm_mark *mark,
-                                u8 mode, u32 reqid, u8 proto,
+                                u8 mode, u32 reqid, u32 if_id, u8 proto,
                                 const xfrm_address_t *daddr,
                                 const xfrm_address_t *saddr, int create,
                                 unsigned short family);
@@ -2019,6 +2021,15 @@ static inline __u32 xfrm_smark_get(__u32 mark, struct xfrm_state *x)
        return (m->v & m->m) | (mark & ~m->m);
 }
 
+static inline int xfrm_if_id_put(struct sk_buff *skb, __u32 if_id)
+{
+       int ret = 0;
+
+       if (if_id)
+               ret = nla_put_u32(skb, XFRMA_IF_ID, if_id);
+       return ret;
+}
+
 static inline int xfrm_tunnel_check(struct sk_buff *skb, struct xfrm_state *x,
                                    unsigned int family)
 {
index 5a6ed7ce5a295712c298482c87564ab88f788d69..5f3b9fec7b5f4491ad9f38beea7447a305ff4fb0 100644 (file)
@@ -307,6 +307,7 @@ enum xfrm_attr_type_t {
        XFRMA_OFFLOAD_DEV,      /* struct xfrm_state_offload */
        XFRMA_SET_MARK,         /* __u32 */
        XFRMA_SET_MARK_MASK,    /* __u32 */
+       XFRMA_IF_ID,            /* __u32 */
        __XFRMA_MAX
 
 #define XFRMA_OUTPUT_MARK XFRMA_SET_MARK       /* Compatibility */
index 49368e21d228c3f0bd6684c8831fc1e4398d56b1..6d37dbf0aa64216dd73475282d439441d549ec09 100644 (file)
@@ -2255,7 +2255,7 @@ static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow)
                        x = xfrm_state_lookup_byspi(pn->net, htonl(pkt_dev->spi), AF_INET);
                } else {
                        /* slow path: we dont already have xfrm_state */
-                       x = xfrm_stateonly_find(pn->net, DUMMY_MARK,
+                       x = xfrm_stateonly_find(pn->net, DUMMY_MARK, 0,
                                                (xfrm_address_t *)&pkt_dev->cur_daddr,
                                                (xfrm_address_t *)&pkt_dev->cur_saddr,
                                                AF_INET,
index 8bdc1cbe490a4ae819db32851ea6a8184b0727b0..398ebcd614a0e6fa619c42d199771e2d82037704 100644 (file)
@@ -1383,7 +1383,7 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_
        }
 
        if (!x)
-               x = xfrm_find_acq(net, &dummy_mark, mode, reqid, proto, xdaddr, xsaddr, 1, family);
+               x = xfrm_find_acq(net, &dummy_mark, mode, reqid, 0, proto, xdaddr, xsaddr, 1, family);
 
        if (x == NULL)
                return -ENOENT;
@@ -2414,7 +2414,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sa
                        return err;
        }
 
-       xp = xfrm_policy_bysel_ctx(net, DUMMY_MARK, XFRM_POLICY_TYPE_MAIN,
+       xp = xfrm_policy_bysel_ctx(net, DUMMY_MARK, 0, XFRM_POLICY_TYPE_MAIN,
                                   pol->sadb_x_policy_dir - 1, &sel, pol_ctx,
                                   1, &err);
        security_xfrm_policy_free(pol_ctx);
@@ -2663,7 +2663,7 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, const struct sadb_
                return -EINVAL;
 
        delete = (hdr->sadb_msg_type == SADB_X_SPDDELETE2);
-       xp = xfrm_policy_byid(net, DUMMY_MARK, XFRM_POLICY_TYPE_MAIN,
+       xp = xfrm_policy_byid(net, DUMMY_MARK, 0, XFRM_POLICY_TYPE_MAIN,
                              dir, pol->sadb_x_policy_id, delete, &err);
        if (xp == NULL)
                return -ENOENT;
index 7637637717ecb4887f5dc529edcd3ec1d96b9f6c..fc0c69312b2cfec2e554458528da677d4dcf9619 100644 (file)
@@ -747,6 +747,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
        newpos = NULL;
        hlist_for_each_entry(pol, chain, bydst) {
                if (pol->type == policy->type &&
+                   pol->if_id == policy->if_id &&
                    !selector_cmp(&pol->selector, &policy->selector) &&
                    xfrm_policy_mark_match(policy, pol) &&
                    xfrm_sec_ctx_match(pol->security, policy->security) &&
@@ -798,8 +799,9 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
 }
 EXPORT_SYMBOL(xfrm_policy_insert);
 
-struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type,
-                                         int dir, struct xfrm_selector *sel,
+struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u32 if_id,
+                                         u8 type, int dir,
+                                         struct xfrm_selector *sel,
                                          struct xfrm_sec_ctx *ctx, int delete,
                                          int *err)
 {
@@ -812,6 +814,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type,
        ret = NULL;
        hlist_for_each_entry(pol, chain, bydst) {
                if (pol->type == type &&
+                   pol->if_id == if_id &&
                    (mark & pol->mark.m) == pol->mark.v &&
                    !selector_cmp(sel, &pol->selector) &&
                    xfrm_sec_ctx_match(ctx, pol->security)) {
@@ -837,8 +840,9 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type,
 }
 EXPORT_SYMBOL(xfrm_policy_bysel_ctx);
 
-struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8 type,
-                                    int dir, u32 id, int delete, int *err)
+struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u32 if_id,
+                                    u8 type, int dir, u32 id, int delete,
+                                    int *err)
 {
        struct xfrm_policy *pol, *ret;
        struct hlist_head *chain;
@@ -853,6 +857,7 @@ struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8 type,
        ret = NULL;
        hlist_for_each_entry(pol, chain, byidx) {
                if (pol->type == type && pol->index == id &&
+                   pol->if_id == if_id &&
                    (mark & pol->mark.m) == pol->mark.v) {
                        xfrm_pol_hold(pol);
                        if (delete) {
@@ -1063,6 +1068,7 @@ static int xfrm_policy_match(const struct xfrm_policy *pol,
        bool match;
 
        if (pol->family != family ||
+           pol->if_id != fl->flowi_xfrm.if_id ||
            (fl->flowi_mark & pol->mark.m) != pol->mark.v ||
            pol->type != type)
                return ret;
@@ -1177,7 +1183,8 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
 
                match = xfrm_selector_match(&pol->selector, fl, family);
                if (match) {
-                       if ((sk->sk_mark & pol->mark.m) != pol->mark.v) {
+                       if ((sk->sk_mark & pol->mark.m) != pol->mark.v ||
+                           pol->if_id != fl->flowi_xfrm.if_id) {
                                pol = NULL;
                                goto out;
                        }
@@ -1305,6 +1312,7 @@ static struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir)
                newp->lft = old->lft;
                newp->curlft = old->curlft;
                newp->mark = old->mark;
+               newp->if_id = old->if_id;
                newp->action = old->action;
                newp->flags = old->flags;
                newp->xfrm_nr = old->xfrm_nr;
index 8308281f32530bd9103e7f7bd06472824600c526..3803b6813fc59da3ff8a68a020d8d6e172f94361 100644 (file)
@@ -941,6 +941,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
        int error = 0;
        struct xfrm_state *best = NULL;
        u32 mark = pol->mark.v & pol->mark.m;
+       u32 if_id = fl->flowi_xfrm.if_id;
        unsigned short encap_family = tmpl->encap_family;
        unsigned int sequence;
        struct km_event c;
@@ -955,6 +956,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
                if (x->props.family == encap_family &&
                    x->props.reqid == tmpl->reqid &&
                    (mark & x->mark.m) == x->mark.v &&
+                   x->if_id == if_id &&
                    !(x->props.flags & XFRM_STATE_WILDRECV) &&
                    xfrm_state_addr_check(x, daddr, saddr, encap_family) &&
                    tmpl->mode == x->props.mode &&
@@ -971,6 +973,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
                if (x->props.family == encap_family &&
                    x->props.reqid == tmpl->reqid &&
                    (mark & x->mark.m) == x->mark.v &&
+                   x->if_id == if_id &&
                    !(x->props.flags & XFRM_STATE_WILDRECV) &&
                    xfrm_addr_equal(&x->id.daddr, daddr, encap_family) &&
                    tmpl->mode == x->props.mode &&
@@ -1010,6 +1013,7 @@ found:
                 * to current session. */
                xfrm_init_tempstate(x, fl, tmpl, daddr, saddr, family);
                memcpy(&x->mark, &pol->mark, sizeof(x->mark));
+               x->if_id = if_id;
 
                error = security_xfrm_state_alloc_acquire(x, pol->security, fl->flowi_secid);
                if (error) {
@@ -1067,7 +1071,7 @@ out:
 }
 
 struct xfrm_state *
-xfrm_stateonly_find(struct net *net, u32 mark,
+xfrm_stateonly_find(struct net *net, u32 mark, u32 if_id,
                    xfrm_address_t *daddr, xfrm_address_t *saddr,
                    unsigned short family, u8 mode, u8 proto, u32 reqid)
 {
@@ -1080,6 +1084,7 @@ xfrm_stateonly_find(struct net *net, u32 mark,
                if (x->props.family == family &&
                    x->props.reqid == reqid &&
                    (mark & x->mark.m) == x->mark.v &&
+                   x->if_id == if_id &&
                    !(x->props.flags & XFRM_STATE_WILDRECV) &&
                    xfrm_state_addr_check(x, daddr, saddr, family) &&
                    mode == x->props.mode &&
@@ -1160,11 +1165,13 @@ static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
        struct xfrm_state *x;
        unsigned int h;
        u32 mark = xnew->mark.v & xnew->mark.m;
+       u32 if_id = xnew->if_id;
 
        h = xfrm_dst_hash(net, &xnew->id.daddr, &xnew->props.saddr, reqid, family);
        hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) {
                if (x->props.family     == family &&
                    x->props.reqid      == reqid &&
+                   x->if_id            == if_id &&
                    (mark & x->mark.m) == x->mark.v &&
                    xfrm_addr_equal(&x->id.daddr, &xnew->id.daddr, family) &&
                    xfrm_addr_equal(&x->props.saddr, &xnew->props.saddr, family))
@@ -1187,7 +1194,7 @@ EXPORT_SYMBOL(xfrm_state_insert);
 static struct xfrm_state *__find_acq_core(struct net *net,
                                          const struct xfrm_mark *m,
                                          unsigned short family, u8 mode,
-                                         u32 reqid, u8 proto,
+                                         u32 reqid, u32 if_id, u8 proto,
                                          const xfrm_address_t *daddr,
                                          const xfrm_address_t *saddr,
                                          int create)
@@ -1242,6 +1249,7 @@ static struct xfrm_state *__find_acq_core(struct net *net,
                x->props.family = family;
                x->props.mode = mode;
                x->props.reqid = reqid;
+               x->if_id = if_id;
                x->mark.v = m->v;
                x->mark.m = m->m;
                x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
@@ -1296,7 +1304,7 @@ int xfrm_state_add(struct xfrm_state *x)
 
        if (use_spi && !x1)
                x1 = __find_acq_core(net, &x->mark, family, x->props.mode,
-                                    x->props.reqid, x->id.proto,
+                                    x->props.reqid, x->if_id, x->id.proto,
                                     &x->id.daddr, &x->props.saddr, 0);
 
        __xfrm_state_bump_genids(x);
@@ -1395,6 +1403,7 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig,
        x->props.flags = orig->props.flags;
        x->props.extra_flags = orig->props.extra_flags;
 
+       x->if_id = orig->if_id;
        x->tfcpad = orig->tfcpad;
        x->replay_maxdiff = orig->replay_maxdiff;
        x->replay_maxage = orig->replay_maxage;
@@ -1619,13 +1628,13 @@ EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
 
 struct xfrm_state *
 xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid,
-             u8 proto, const xfrm_address_t *daddr,
+             u32 if_id, u8 proto, const xfrm_address_t *daddr,
              const xfrm_address_t *saddr, int create, unsigned short family)
 {
        struct xfrm_state *x;
 
        spin_lock_bh(&net->xfrm.xfrm_state_lock);
-       x = __find_acq_core(net, mark, family, mode, reqid, proto, daddr, saddr, create);
+       x = __find_acq_core(net, mark, family, mode, reqid, if_id, proto, daddr, saddr, create);
        spin_unlock_bh(&net->xfrm.xfrm_state_lock);
 
        return x;
index 9602cc9e05ab1d935965272a012a4573254b206a..79245e1c34873547ee72cb9336150037698a0789 100644 (file)
@@ -594,6 +594,9 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
 
        xfrm_smark_init(attrs, &x->props.smark);
 
+       if (attrs[XFRMA_IF_ID])
+               x->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
+
        err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV]);
        if (err)
                goto error;
@@ -929,7 +932,11 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
                ret = copy_user_offload(&x->xso, skb);
        if (ret)
                goto out;
-
+       if (x->if_id) {
+               ret = nla_put_u32(skb, XFRMA_IF_ID, x->if_id);
+               if (ret)
+                       goto out;
+       }
        if (x->security)
                ret = copy_sec_ctx(x->security, skb);
 out:
@@ -1278,6 +1285,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
        int err;
        u32 mark;
        struct xfrm_mark m;
+       u32 if_id = 0;
 
        p = nlmsg_data(nlh);
        err = verify_spi_info(p->info.id.proto, p->min, p->max);
@@ -1290,6 +1298,10 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
        x = NULL;
 
        mark = xfrm_mark_get(attrs, &m);
+
+       if (attrs[XFRMA_IF_ID])
+               if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
+
        if (p->info.seq) {
                x = xfrm_find_acq_byseq(net, mark, p->info.seq);
                if (x && !xfrm_addr_equal(&x->id.daddr, daddr, family)) {
@@ -1300,7 +1312,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
 
        if (!x)
                x = xfrm_find_acq(net, &m, p->info.mode, p->info.reqid,
-                                 p->info.id.proto, daddr,
+                                 if_id, p->info.id.proto, daddr,
                                  &p->info.saddr, 1,
                                  family);
        err = -ENOENT;
@@ -1588,6 +1600,9 @@ static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_us
 
        xfrm_mark_get(attrs, &xp->mark);
 
+       if (attrs[XFRMA_IF_ID])
+               xp->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
+
        return xp;
  error:
        *errp = err;
@@ -1733,6 +1748,8 @@ static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr
                err = copy_to_user_policy_type(xp->type, skb);
        if (!err)
                err = xfrm_mark_put(skb, &xp->mark);
+       if (!err)
+               err = xfrm_if_id_put(skb, xp->if_id);
        if (err) {
                nlmsg_cancel(skb, nlh);
                return err;
@@ -1814,6 +1831,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
        int delete;
        struct xfrm_mark m;
        u32 mark = xfrm_mark_get(attrs, &m);
+       u32 if_id = 0;
 
        p = nlmsg_data(nlh);
        delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY;
@@ -1826,8 +1844,11 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
        if (err)
                return err;
 
+       if (attrs[XFRMA_IF_ID])
+               if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
+
        if (p->index)
-               xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, delete, &err);
+               xp = xfrm_policy_byid(net, mark, if_id, type, p->dir, p->index, delete, &err);
        else {
                struct nlattr *rt = attrs[XFRMA_SEC_CTX];
                struct xfrm_sec_ctx *ctx;
@@ -1844,7 +1865,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
                        if (err)
                                return err;
                }
-               xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, &p->sel,
+               xp = xfrm_policy_bysel_ctx(net, mark, if_id, type, p->dir, &p->sel,
                                           ctx, delete, &err);
                security_xfrm_policy_free(ctx);
        }
@@ -1967,6 +1988,10 @@ static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct
        if (err)
                goto out_cancel;
 
+       err = xfrm_if_id_put(skb, x->if_id);
+       if (err)
+               goto out_cancel;
+
        nlmsg_end(skb, nlh);
        return 0;
 
@@ -2109,6 +2134,7 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
        int err = -ENOENT;
        struct xfrm_mark m;
        u32 mark = xfrm_mark_get(attrs, &m);
+       u32 if_id = 0;
 
        err = copy_from_user_policy_type(&type, attrs);
        if (err)
@@ -2118,8 +2144,11 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
        if (err)
                return err;
 
+       if (attrs[XFRMA_IF_ID])
+               if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
+
        if (p->index)
-               xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, 0, &err);
+               xp = xfrm_policy_byid(net, mark, if_id, type, p->dir, p->index, 0, &err);
        else {
                struct nlattr *rt = attrs[XFRMA_SEC_CTX];
                struct xfrm_sec_ctx *ctx;
@@ -2136,7 +2165,7 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
                        if (err)
                                return err;
                }
-               xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir,
+               xp = xfrm_policy_bysel_ctx(net, mark, if_id, type, p->dir,
                                           &p->sel, ctx, 0, &err);
                security_xfrm_policy_free(ctx);
        }
@@ -2520,6 +2549,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
        [XFRMA_OFFLOAD_DEV]     = { .len = sizeof(struct xfrm_user_offload) },
        [XFRMA_SET_MARK]        = { .type = NLA_U32 },
        [XFRMA_SET_MARK_MASK]   = { .type = NLA_U32 },
+       [XFRMA_IF_ID]           = { .type = NLA_U32 },
 };
 
 static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = {
@@ -2651,6 +2681,10 @@ static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct
        if (err)
                return err;
 
+       err = xfrm_if_id_put(skb, x->if_id);
+       if (err)
+               return err;
+
        nlmsg_end(skb, nlh);
        return 0;
 }
@@ -2749,6 +2783,8 @@ static inline unsigned int xfrm_sa_len(struct xfrm_state *x)
                l += nla_total_size(sizeof(x->props.smark.v));
                l += nla_total_size(sizeof(x->props.smark.m));
        }
+       if (x->if_id)
+               l += nla_total_size(sizeof(x->if_id));
 
        /* Must count x->lastused as it may become non-zero behind our back. */
        l += nla_total_size_64bit(sizeof(u64));
@@ -2878,6 +2914,8 @@ static int build_acquire(struct sk_buff *skb, struct xfrm_state *x,
                err = copy_to_user_policy_type(xp->type, skb);
        if (!err)
                err = xfrm_mark_put(skb, &xp->mark);
+       if (!err)
+               err = xfrm_if_id_put(skb, xp->if_id);
        if (err) {
                nlmsg_cancel(skb, nlh);
                return err;
@@ -2994,6 +3032,8 @@ static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp,
                err = copy_to_user_policy_type(xp->type, skb);
        if (!err)
                err = xfrm_mark_put(skb, &xp->mark);
+       if (!err)
+               err = xfrm_if_id_put(skb, xp->if_id);
        if (err) {
                nlmsg_cancel(skb, nlh);
                return err;
@@ -3075,6 +3115,8 @@ static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_e
                err = copy_to_user_policy_type(xp->type, skb);
        if (!err)
                err = xfrm_mark_put(skb, &xp->mark);
+       if (!err)
+               err = xfrm_if_id_put(skb, xp->if_id);
        if (err)
                goto out_free_skb;