return 0;
}
+static void xso_to_xuo(const struct xfrm_dev_offload *xso,
+ struct xfrm_user_offload *xuo)
+{
+ xuo->ifindex = xso->dev->ifindex;
+ if (xso->dir == XFRM_DEV_OFFLOAD_IN)
+ xuo->flags = XFRM_OFFLOAD_INBOUND;
+ if (xso->type == XFRM_DEV_OFFLOAD_PACKET)
+ xuo->flags |= XFRM_OFFLOAD_PACKET;
+}
+
static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb)
{
struct xfrm_user_offload *xuo;
xuo = nla_data(attr);
memset(xuo, 0, sizeof(*xuo));
- xuo->ifindex = xso->dev->ifindex;
- if (xso->dir == XFRM_DEV_OFFLOAD_IN)
- xuo->flags = XFRM_OFFLOAD_INBOUND;
- if (xso->type == XFRM_DEV_OFFLOAD_PACKET)
- xuo->flags |= XFRM_OFFLOAD_PACKET;
+ xso_to_xuo(xso, xuo);
return 0;
}
return 0;
}
-static int xfrm_smark_put(struct sk_buff *skb, struct xfrm_mark *m)
+static int xfrm_smark_put(struct sk_buff *skb, const struct xfrm_mark *m)
{
int ret = 0;
}
#ifdef CONFIG_XFRM_MIGRATE
+static void copy_from_user_migrate_state(struct xfrm_migrate *ma,
+ const struct xfrm_user_migrate_state *um)
+{
+ memcpy(&ma->old_daddr, &um->id.daddr, sizeof(ma->old_daddr));
+ memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr));
+ memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr));
+
+ ma->proto = um->id.proto;
+ ma->new_reqid = um->new_reqid;
+
+ ma->old_family = um->id.family;
+ ma->new_family = um->new_family;
+
+ ma->old_mark = um->old_mark;
+ ma->flags = um->flags;
+ ma->new_sel = &um->new_sel;
+ ma->msg_type = XFRM_MSG_MIGRATE_STATE;
+}
+
static int copy_from_user_migrate(struct xfrm_migrate *ma,
struct xfrm_kmaddress *k,
struct nlattr **attrs, int *num,
ma->old_family = um->old_family;
ma->new_family = um->new_family;
+ ma->msg_type = XFRM_MSG_MIGRATE;
}
*num = i;
struct nlattr **attrs, struct netlink_ext_ack *extack)
{
struct xfrm_userpolicy_id *pi = nlmsg_data(nlh);
- struct xfrm_migrate m[XFRM_MAX_DEPTH];
+ struct xfrm_migrate m[XFRM_MAX_DEPTH] = {};
struct xfrm_kmaddress km, *kmp;
u8 type;
int err;
kfree(xuo);
return err;
}
+
+static int build_migrate_state(struct sk_buff *skb,
+ const struct xfrm_user_migrate_state *um,
+ const struct xfrm_migrate *m,
+ u8 dir, u32 portid, u32 seq)
+{
+ int err;
+ struct nlmsghdr *nlh;
+ struct xfrm_user_migrate_state *hdr;
+
+ nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_MIGRATE_STATE,
+ sizeof(struct xfrm_user_migrate_state), 0);
+ if (!nlh)
+ return -EMSGSIZE;
+
+ hdr = nlmsg_data(nlh);
+ *hdr = *um;
+ hdr->new_sel = *m->new_sel;
+
+ if (m->encap) {
+ err = nla_put(skb, XFRMA_ENCAP, sizeof(*m->encap), m->encap);
+ if (err)
+ goto out_cancel;
+ }
+
+ if (m->xuo) {
+ err = nla_put(skb, XFRMA_OFFLOAD_DEV, sizeof(*m->xuo), m->xuo);
+ if (err)
+ goto out_cancel;
+ }
+
+ if (m->new_mark) {
+ err = nla_put(skb, XFRMA_MARK, sizeof(*m->new_mark),
+ m->new_mark);
+ if (err)
+ goto out_cancel;
+ }
+
+ err = xfrm_smark_put(skb, &m->smark);
+ if (err)
+ goto out_cancel;
+
+ if (m->mapping_maxage) {
+ err = nla_put_u32(skb, XFRMA_MTIMER_THRESH, m->mapping_maxage);
+ if (err)
+ goto out_cancel;
+ }
+
+ if (m->nat_keepalive_interval) {
+ err = nla_put_u32(skb, XFRMA_NAT_KEEPALIVE_INTERVAL,
+ m->nat_keepalive_interval);
+ if (err)
+ goto out_cancel;
+ }
+
+ if (dir) {
+ err = nla_put_u8(skb, XFRMA_SA_DIR, dir);
+ if (err)
+ goto out_cancel;
+ }
+
+ nlmsg_end(skb, nlh);
+ return 0;
+
+out_cancel:
+ nlmsg_cancel(skb, nlh);
+ return err;
+}
+
+static unsigned int xfrm_migrate_state_msgsize(const struct xfrm_migrate *m,
+ u8 dir)
+{
+ return NLMSG_ALIGN(sizeof(struct xfrm_user_migrate_state)) +
+ (m->encap ? nla_total_size(sizeof(struct xfrm_encap_tmpl)) : 0) +
+ (m->xuo ? nla_total_size(sizeof(struct xfrm_user_offload)) : 0) +
+ (m->new_mark ? nla_total_size(sizeof(struct xfrm_mark)) : 0) +
+ ((m->smark.v | m->smark.m) ? nla_total_size(sizeof(u32)) * 2 : 0) +
+ (m->mapping_maxage ? nla_total_size(sizeof(u32)) : 0) +
+ (m->nat_keepalive_interval ? nla_total_size(sizeof(u32)) : 0) +
+ (dir ? nla_total_size(sizeof(u8)) : 0); /* XFRMA_SA_DIR */
+}
+
+static int xfrm_send_migrate_state(struct net *net,
+ const struct xfrm_user_migrate_state *um,
+ const struct xfrm_migrate *m,
+ u8 dir, u32 portid, u32 seq)
+{
+ int err;
+ struct sk_buff *skb;
+
+ skb = nlmsg_new(xfrm_migrate_state_msgsize(m, dir), GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ err = build_migrate_state(skb, um, m, dir, portid, seq);
+ if (err < 0) {
+ kfree_skb(skb);
+ return err;
+ }
+
+ return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE);
+}
+
+static int xfrm_do_migrate_state(struct sk_buff *skb, struct nlmsghdr *nlh,
+ struct nlattr **attrs, struct netlink_ext_ack *extack)
+{
+ struct xfrm_user_migrate_state *um = nlmsg_data(nlh);
+ struct net *net = sock_net(skb->sk);
+ struct xfrm_user_offload xuo = {};
+ struct xfrm_migrate m = {};
+ struct xfrm_state *xc;
+ struct xfrm_state *x;
+ int err;
+
+ if (!um->id.spi) {
+ NL_SET_ERR_MSG(extack, "Invalid SPI 0x0");
+ return -EINVAL;
+ }
+
+ if (um->reserved) {
+ NL_SET_ERR_MSG(extack, "Reserved field must be zero");
+ return -EINVAL;
+ }
+
+ if (um->flags & ~XFRM_MIGRATE_STATE_KNOWN_FLAGS) {
+ NL_SET_ERR_MSG_FMT(extack, "Unknown flags: 0x%x",
+ um->flags & ~XFRM_MIGRATE_STATE_KNOWN_FLAGS);
+ return -EINVAL;
+ }
+
+ err = verify_xfrm_family(um->new_family, extack);
+ if (err)
+ return err;
+
+ if (!(um->flags & XFRM_MIGRATE_STATE_UPDATE_H2H_SEL)) {
+ err = verify_selector_prefixlen(um->new_sel.family,
+ &um->new_sel, extack);
+ if (err)
+ return err;
+ }
+
+ copy_from_user_migrate_state(&m, um);
+
+ x = xfrm_state_lookup(net, m.old_mark.v & m.old_mark.m,
+ &um->id.daddr, um->id.spi,
+ um->id.proto, um->id.family);
+ if (!x) {
+ NL_SET_ERR_MSG(extack, "Can not find state");
+ return -ESRCH;
+ }
+
+ if (um->flags & XFRM_MIGRATE_STATE_UPDATE_H2H_SEL) {
+ u8 prefixlen = (x->props.family == AF_INET6) ? 128 : 32;
+
+ if (x->sel.prefixlen_s != x->sel.prefixlen_d ||
+ x->sel.prefixlen_d != prefixlen ||
+ !xfrm_addr_equal(&x->sel.daddr, &x->id.daddr, x->props.family) ||
+ !xfrm_addr_equal(&x->sel.saddr, &x->props.saddr, x->props.family)) {
+ NL_SET_ERR_MSG(extack,
+ "SA selector is not a single-host match for SA addresses");
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (attrs[XFRMA_ENCAP]) {
+ m.encap = nla_data(attrs[XFRMA_ENCAP]);
+ if (m.encap->encap_type == 0) {
+ m.encap = NULL; /* sentinel: remove encap */
+ } else if (m.encap->encap_type != UDP_ENCAP_ESPINUDP) {
+ NL_SET_ERR_MSG(extack, "Unsupported encapsulation type");
+ err = -EINVAL;
+ goto out;
+ }
+ } else {
+ m.encap = x->encap; /* omit-to-inherit */
+ }
+
+ if (attrs[XFRMA_MTIMER_THRESH]) {
+ err = verify_mtimer_thresh(!!m.encap, x->dir, extack);
+ if (err)
+ goto out;
+ }
+
+ if (nla_get_u32_default(attrs[XFRMA_NAT_KEEPALIVE_INTERVAL], 0) && !m.encap) {
+ NL_SET_ERR_MSG(extack,
+ "NAT_KEEPALIVE_INTERVAL requires encapsulation");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (attrs[XFRMA_OFFLOAD_DEV]) {
+ m.xuo = nla_data(attrs[XFRMA_OFFLOAD_DEV]);
+ } else {
+ bool inherit_offload = !(um->flags & XFRM_MIGRATE_STATE_CLEAR_OFFLOAD);
+
+ if (inherit_offload && x->xso.dev) {
+ xso_to_xuo(&x->xso, &xuo);
+ m.xuo = &xuo;
+ }
+ }
+
+ if (attrs[XFRMA_MARK])
+ m.new_mark = nla_data(attrs[XFRMA_MARK]);
+
+ if (attrs[XFRMA_SET_MARK])
+ xfrm_smark_init(attrs, &m.smark);
+ else
+ m.smark = x->props.smark;
+
+ m.mapping_maxage = nla_get_u32_default(attrs[XFRMA_MTIMER_THRESH],
+ x->mapping_maxage);
+ m.nat_keepalive_interval = nla_get_u32_default(attrs[XFRMA_NAT_KEEPALIVE_INTERVAL],
+ x->nat_keepalive_interval);
+
+ if (m.new_family != um->id.family ||
+ !xfrm_addr_equal(&m.new_daddr, &um->id.daddr, um->id.family)) {
+ u32 new_mark_key = m.new_mark ? m.new_mark->v & m.new_mark->m :
+ m.old_mark.v & m.old_mark.m;
+ struct xfrm_state *x_new;
+
+ x_new = xfrm_state_lookup(net, new_mark_key, &m.new_daddr,
+ um->id.spi, um->id.proto, m.new_family);
+ if (x_new) {
+ xfrm_state_put(x_new);
+ NL_SET_ERR_MSG(extack, "New SA tuple already occupied");
+ err = -EEXIST;
+ goto out;
+ }
+ }
+
+ xc = xfrm_state_migrate_create(x, &m, net, extack);
+ if (!xc) {
+ NL_SET_ERR_MSG_WEAK(extack, "State migration clone failed");
+ err = -EINVAL;
+ goto out;
+ }
+
+ spin_lock_bh(&x->lock);
+ if (x->km.state != XFRM_STATE_VALID) {
+ spin_unlock_bh(&x->lock);
+ NL_SET_ERR_MSG(extack, "State already deleted");
+ err = -ESRCH;
+ goto out_xc;
+ }
+ xfrm_migrate_sync(xc, x); /* to prevent SN/IV reuse */
+ __xfrm_state_delete(x);
+ spin_unlock_bh(&x->lock);
+
+ err = xfrm_state_migrate_install(x, xc, &m, extack);
+ if (err < 0) {
+ /*
+ * Should not occur: pre-check above ensures the new tuple is
+ * free under xfrm_cfg_mutex. Both SAs are gone if it does;
+ * restoring x would risk SN/IV reuse.
+ */
+ goto out;
+ }
+
+ /* Restore encap cleared by sentinel (type=0) during migration. */
+ if (attrs[XFRMA_ENCAP])
+ m.encap = nla_data(attrs[XFRMA_ENCAP]);
+
+ m.new_sel = &xc->sel;
+ m.mapping_maxage = xc->mapping_maxage;
+ m.nat_keepalive_interval = xc->nat_keepalive_interval;
+
+ err = xfrm_send_migrate_state(net, um, &m, xc->dir,
+ nlh->nlmsg_pid, nlh->nlmsg_seq);
+ if (err < 0) {
+ NL_SET_ERR_MSG(extack, "Failed to send migration notification");
+ err = 0;
+ }
+
+out:
+ xfrm_state_put(x);
+ return err;
+out_xc:
+ xc->km.state = XFRM_STATE_DEAD;
+ xfrm_state_put(xc);
+ xfrm_state_put(x);
+ return err;
+}
+
#else
+static int xfrm_do_migrate_state(struct sk_buff *skb, struct nlmsghdr *nlh,
+ struct nlattr **attrs, struct netlink_ext_ack *extack)
+{
+ NL_SET_ERR_MSG(extack, "XFRM_MSG_MIGRATE_STATE is not supported");
+ return -ENOPROTOOPT;
+}
+
static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs, struct netlink_ext_ack *extack)
{
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
[XFRM_MSG_SETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default),
[XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default),
+ [XFRM_MSG_MIGRATE_STATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_migrate_state),
};
EXPORT_SYMBOL_GPL(xfrm_msg_min);
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo },
[XFRM_MSG_SETDEFAULT - XFRM_MSG_BASE] = { .doit = xfrm_set_default },
[XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = { .doit = xfrm_get_default },
+ [XFRM_MSG_MIGRATE_STATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate_state },
};
static int xfrm_reject_unused_attr(int type, struct nlattr **attrs,