Commit b198d7b4 authored by Steffen Klassert's avatar Steffen Klassert
Browse files

Merge branch 'xfrm: add extack support to some more message types'



Sabrina Dubroca says:

============
This is the last part of my extack work for xfrm, adding extack
messages to the last remaining operations: NEWSPDINFO, ALLOCSPI,
MIGRATE, NEWAE, DELSA, EXPIRE.

The first patch does a few clean ups on code that will be changed
later on it the series.
============

Signed-off-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
parents cc2bbbfd a7417216
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -1681,8 +1681,9 @@ struct xfrm_policy *xfrm_policy_byid(struct net *net,
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);
int verify_spi_info(u8 proto, u32 min, u32 max, struct netlink_ext_ack *extack);
int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi,
		   struct netlink_ext_ack *extack);
struct xfrm_state *xfrm_find_acq(struct net *net, const struct xfrm_mark *mark,
				 u8 mode, u32 reqid, u32 if_id, u8 proto,
				 const xfrm_address_t *daddr,
@@ -1703,7 +1704,8 @@ struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
		 struct xfrm_migrate *m, int num_bundles,
		 struct xfrm_kmaddress *k, struct net *net,
		 struct xfrm_encap_tmpl *encap, u32 if_id);
		 struct xfrm_encap_tmpl *encap, u32 if_id,
		 struct netlink_ext_ack *extack);
#endif

int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport);
+3 −3
Original line number Diff line number Diff line
@@ -1377,13 +1377,13 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_
		max_spi = range->sadb_spirange_max;
	}

	err = verify_spi_info(x->id.proto, min_spi, max_spi);
	err = verify_spi_info(x->id.proto, min_spi, max_spi, NULL);
	if (err) {
		xfrm_state_put(x);
		return err;
	}

	err = xfrm_alloc_spi(x, min_spi, max_spi);
	err = xfrm_alloc_spi(x, min_spi, max_spi, NULL);
	resp_skb = err ? ERR_PTR(err) : pfkey_xfrm_state2msg(x);

	if (IS_ERR(resp_skb)) {
@@ -2626,7 +2626,7 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,
	}

	return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i,
			    kma ? &k : NULL, net, NULL, 0);
			    kma ? &k : NULL, net, NULL, 0, NULL);

 out:
	return err;
+24 −9
Original line number Diff line number Diff line
@@ -4333,7 +4333,8 @@ static int migrate_tmpl_match(const struct xfrm_migrate *m, const struct xfrm_tm

/* update endpoint address(es) of template(s) */
static int xfrm_policy_migrate(struct xfrm_policy *pol,
			       struct xfrm_migrate *m, int num_migrate)
			       struct xfrm_migrate *m, int num_migrate,
			       struct netlink_ext_ack *extack)
{
	struct xfrm_migrate *mp;
	int i, j, n = 0;
@@ -4341,6 +4342,7 @@ static int xfrm_policy_migrate(struct xfrm_policy *pol,
	write_lock_bh(&pol->lock);
	if (unlikely(pol->walk.dead)) {
		/* target policy has been deleted */
		NL_SET_ERR_MSG(extack, "Target policy not found");
		write_unlock_bh(&pol->lock);
		return -ENOENT;
	}
@@ -4372,17 +4374,22 @@ static int xfrm_policy_migrate(struct xfrm_policy *pol,
	return 0;
}

static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate)
static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate,
			      struct netlink_ext_ack *extack)
{
	int i, j;

	if (num_migrate < 1 || num_migrate > XFRM_MAX_DEPTH)
	if (num_migrate < 1 || num_migrate > XFRM_MAX_DEPTH) {
		NL_SET_ERR_MSG(extack, "Invalid number of SAs to migrate, must be 0 < num <= XFRM_MAX_DEPTH (6)");
		return -EINVAL;
	}

	for (i = 0; i < num_migrate; i++) {
		if (xfrm_addr_any(&m[i].new_daddr, m[i].new_family) ||
		    xfrm_addr_any(&m[i].new_saddr, m[i].new_family))
		    xfrm_addr_any(&m[i].new_saddr, m[i].new_family)) {
			NL_SET_ERR_MSG(extack, "Addresses in the MIGRATE attribute's list cannot be null");
			return -EINVAL;
		}

		/* check if there is any duplicated entry */
		for (j = i + 1; j < num_migrate; j++) {
@@ -4393,10 +4400,12 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate)
			    m[i].proto == m[j].proto &&
			    m[i].mode == m[j].mode &&
			    m[i].reqid == m[j].reqid &&
			    m[i].old_family == m[j].old_family)
			    m[i].old_family == m[j].old_family) {
				NL_SET_ERR_MSG(extack, "Entries in the MIGRATE attribute's list must be unique");
				return -EINVAL;
			}
		}
	}

	return 0;
}
@@ -4404,7 +4413,8 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate)
int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
		 struct xfrm_migrate *m, int num_migrate,
		 struct xfrm_kmaddress *k, struct net *net,
		 struct xfrm_encap_tmpl *encap, u32 if_id)
		 struct xfrm_encap_tmpl *encap, u32 if_id,
		 struct netlink_ext_ack *extack)
{
	int i, err, nx_cur = 0, nx_new = 0;
	struct xfrm_policy *pol = NULL;
@@ -4414,16 +4424,20 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
	struct xfrm_migrate *mp;

	/* Stage 0 - sanity checks */
	if ((err = xfrm_migrate_check(m, num_migrate)) < 0)
	err = xfrm_migrate_check(m, num_migrate, extack);
	if (err < 0)
		goto out;

	if (dir >= XFRM_POLICY_MAX) {
		NL_SET_ERR_MSG(extack, "Invalid policy direction");
		err = -EINVAL;
		goto out;
	}

	/* Stage 1 - find policy */
	if ((pol = xfrm_migrate_policy_find(sel, dir, type, net, if_id)) == NULL) {
	pol = xfrm_migrate_policy_find(sel, dir, type, net, if_id);
	if (!pol) {
		NL_SET_ERR_MSG(extack, "Target policy not found");
		err = -ENOENT;
		goto out;
	}
@@ -4445,7 +4459,8 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
	}

	/* Stage 3 - update policy */
	if ((err = xfrm_policy_migrate(pol, m, num_migrate)) < 0)
	err = xfrm_policy_migrate(pol, m, num_migrate, extack);
	if (err < 0)
		goto restore_state;

	/* Stage 4 - delete old state(s) */
+16 −5
Original line number Diff line number Diff line
@@ -2017,7 +2017,7 @@ u32 xfrm_get_acqseq(void)
}
EXPORT_SYMBOL(xfrm_get_acqseq);

int verify_spi_info(u8 proto, u32 min, u32 max)
int verify_spi_info(u8 proto, u32 min, u32 max, struct netlink_ext_ack *extack)
{
	switch (proto) {
	case IPPROTO_AH:
@@ -2026,22 +2026,28 @@ int verify_spi_info(u8 proto, u32 min, u32 max)

	case IPPROTO_COMP:
		/* IPCOMP spi is 16-bits. */
		if (max >= 0x10000)
		if (max >= 0x10000) {
			NL_SET_ERR_MSG(extack, "IPCOMP SPI must be <= 65535");
			return -EINVAL;
		}
		break;

	default:
		NL_SET_ERR_MSG(extack, "Invalid protocol, must be one of AH, ESP, IPCOMP");
		return -EINVAL;
	}

	if (min > max)
	if (min > max) {
		NL_SET_ERR_MSG(extack, "Invalid SPI range: min > max");
		return -EINVAL;
	}

	return 0;
}
EXPORT_SYMBOL(verify_spi_info);

int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high,
		   struct netlink_ext_ack *extack)
{
	struct net *net = xs_net(x);
	unsigned int h;
@@ -2053,8 +2059,10 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
	u32 mark = x->mark.v & x->mark.m;

	spin_lock_bh(&x->lock);
	if (x->km.state == XFRM_STATE_DEAD)
	if (x->km.state == XFRM_STATE_DEAD) {
		NL_SET_ERR_MSG(extack, "Target ACQUIRE is in DEAD state");
		goto unlock;
	}

	err = 0;
	if (x->id.spi)
@@ -2065,6 +2073,7 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
	if (minspi == maxspi) {
		x0 = xfrm_state_lookup(net, mark, &x->id.daddr, minspi, x->id.proto, x->props.family);
		if (x0) {
			NL_SET_ERR_MSG(extack, "Requested SPI is already in use");
			xfrm_state_put(x0);
			goto unlock;
		}
@@ -2089,6 +2098,8 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
		spin_unlock_bh(&net->xfrm.xfrm_state_lock);

		err = 0;
	} else {
		NL_SET_ERR_MSG(extack, "No SPI available in the requested range");
	}

unlock:
+61 −23
Original line number Diff line number Diff line
@@ -515,7 +515,8 @@ static int attach_aead(struct xfrm_state *x, struct nlattr *rta,
}

static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_esn,
					 struct nlattr *rp)
					 struct nlattr *rp,
					 struct netlink_ext_ack *extack)
{
	struct xfrm_replay_state_esn *up;
	unsigned int ulen;
@@ -528,13 +529,25 @@ static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_es

	/* Check the overall length and the internal bitmap length to avoid
	 * potential overflow. */
	if (nla_len(rp) < (int)ulen ||
	    xfrm_replay_state_esn_len(replay_esn) != ulen ||
	    replay_esn->bmp_len != up->bmp_len)
	if (nla_len(rp) < (int)ulen) {
		NL_SET_ERR_MSG(extack, "ESN attribute is too short");
		return -EINVAL;
	}

	if (xfrm_replay_state_esn_len(replay_esn) != ulen) {
		NL_SET_ERR_MSG(extack, "New ESN size doesn't match the existing SA's ESN size");
		return -EINVAL;
	}

	if (replay_esn->bmp_len != up->bmp_len) {
		NL_SET_ERR_MSG(extack, "New ESN bitmap size doesn't match the existing SA's ESN bitmap");
		return -EINVAL;
	}

	if (up->replay_window > up->bmp_len * sizeof(__u32) * 8)
	if (up->replay_window > up->bmp_len * sizeof(__u32) * 8) {
		NL_SET_ERR_MSG(extack, "ESN replay window is longer than the bitmap");
		return -EINVAL;
	}

	return 0;
}
@@ -862,12 +875,12 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
		goto out;

	if (xfrm_state_kern(x)) {
		NL_SET_ERR_MSG(extack, "SA is in use by tunnels");
		err = -EPERM;
		goto out;
	}

	err = xfrm_state_delete(x);

	if (err < 0)
		goto out;

@@ -1354,21 +1367,29 @@ static int xfrm_set_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
	if (attrs[XFRMA_SPD_IPV4_HTHRESH]) {
		struct nlattr *rta = attrs[XFRMA_SPD_IPV4_HTHRESH];

		if (nla_len(rta) < sizeof(*thresh4))
		if (nla_len(rta) < sizeof(*thresh4)) {
			NL_SET_ERR_MSG(extack, "Invalid SPD_IPV4_HTHRESH attribute length");
			return -EINVAL;
		}
		thresh4 = nla_data(rta);
		if (thresh4->lbits > 32 || thresh4->rbits > 32)
		if (thresh4->lbits > 32 || thresh4->rbits > 32) {
			NL_SET_ERR_MSG(extack, "Invalid hash threshold (must be <= 32 for IPv4)");
			return -EINVAL;
		}
	}
	if (attrs[XFRMA_SPD_IPV6_HTHRESH]) {
		struct nlattr *rta = attrs[XFRMA_SPD_IPV6_HTHRESH];

		if (nla_len(rta) < sizeof(*thresh6))
		if (nla_len(rta) < sizeof(*thresh6)) {
			NL_SET_ERR_MSG(extack, "Invalid SPD_IPV6_HTHRESH attribute length");
			return -EINVAL;
		}
		thresh6 = nla_data(rta);
		if (thresh6->lbits > 128 || thresh6->rbits > 128)
		if (thresh6->lbits > 128 || thresh6->rbits > 128) {
			NL_SET_ERR_MSG(extack, "Invalid hash threshold (must be <= 128 for IPv6)");
			return -EINVAL;
		}
	}

	if (thresh4 || thresh6) {
		write_seqlock(&net->xfrm.policy_hthresh.lock);
@@ -1510,7 +1531,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
	u32 if_id = 0;

	p = nlmsg_data(nlh);
	err = verify_spi_info(p->info.id.proto, p->min, p->max);
	err = verify_spi_info(p->info.id.proto, p->min, p->max, extack);
	if (err)
		goto out_noput;

@@ -1538,10 +1559,12 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
				  &p->info.saddr, 1,
				  family);
	err = -ENOENT;
	if (x == NULL)
	if (!x) {
		NL_SET_ERR_MSG(extack, "Target ACQUIRE not found");
		goto out_noput;
	}

	err = xfrm_alloc_spi(x, p->min, p->max);
	err = xfrm_alloc_spi(x, p->min, p->max, extack);
	if (err)
		goto out;

@@ -2433,12 +2456,16 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
	struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
	struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];

	if (!lt && !rp && !re && !et && !rt)
	if (!lt && !rp && !re && !et && !rt) {
		NL_SET_ERR_MSG(extack, "Missing required attribute for AE");
		return err;
	}

	/* pedantic mode - thou shalt sayeth replaceth */
	if (!(nlh->nlmsg_flags&NLM_F_REPLACE))
	if (!(nlh->nlmsg_flags & NLM_F_REPLACE)) {
		NL_SET_ERR_MSG(extack, "NLM_F_REPLACE flag is required");
		return err;
	}

	mark = xfrm_mark_get(attrs, &m);

@@ -2446,10 +2473,12 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
	if (x == NULL)
		return -ESRCH;

	if (x->km.state != XFRM_STATE_VALID)
	if (x->km.state != XFRM_STATE_VALID) {
		NL_SET_ERR_MSG(extack, "SA must be in VALID state");
		goto out;
	}

	err = xfrm_replay_verify_len(x->replay_esn, re);
	err = xfrm_replay_verify_len(x->replay_esn, re, extack);
	if (err)
		goto out;

@@ -2584,8 +2613,11 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh,

	spin_lock_bh(&x->lock);
	err = -EINVAL;
	if (x->km.state != XFRM_STATE_VALID)
	if (x->km.state != XFRM_STATE_VALID) {
		NL_SET_ERR_MSG(extack, "SA must be in VALID state");
		goto out;
	}

	km_state_expired(x, ue->hard, nlh->nlmsg_pid);

	if (ue->hard) {
@@ -2665,7 +2697,8 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
#ifdef CONFIG_XFRM_MIGRATE
static int copy_from_user_migrate(struct xfrm_migrate *ma,
				  struct xfrm_kmaddress *k,
				  struct nlattr **attrs, int *num)
				  struct nlattr **attrs, int *num,
				  struct netlink_ext_ack *extack)
{
	struct nlattr *rt = attrs[XFRMA_MIGRATE];
	struct xfrm_user_migrate *um;
@@ -2684,8 +2717,10 @@ static int copy_from_user_migrate(struct xfrm_migrate *ma,
	um = nla_data(rt);
	num_migrate = nla_len(rt) / sizeof(*um);

	if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH)
	if (num_migrate <= 0 || num_migrate > XFRM_MAX_DEPTH) {
		NL_SET_ERR_MSG(extack, "Invalid number of SAs to migrate, must be 0 < num <= XFRM_MAX_DEPTH (6)");
		return -EINVAL;
	}

	for (i = 0; i < num_migrate; i++, um++, ma++) {
		memcpy(&ma->old_daddr, &um->old_daddr, sizeof(ma->old_daddr));
@@ -2718,8 +2753,10 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
	struct xfrm_encap_tmpl  *encap = NULL;
	u32 if_id = 0;

	if (attrs[XFRMA_MIGRATE] == NULL)
	if (!attrs[XFRMA_MIGRATE]) {
		NL_SET_ERR_MSG(extack, "Missing required MIGRATE attribute");
		return -EINVAL;
	}

	kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL;

@@ -2727,7 +2764,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
	if (err)
		return err;

	err = copy_from_user_migrate((struct xfrm_migrate *)m, kmp, attrs, &n);
	err = copy_from_user_migrate(m, kmp, attrs, &n, extack);
	if (err)
		return err;

@@ -2744,7 +2781,8 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
	if (attrs[XFRMA_IF_ID])
		if_id = nla_get_u32(attrs[XFRMA_IF_ID]);

	err = xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp, net, encap, if_id);
	err = xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp, net, encap,
			   if_id, extack);

	kfree(encap);