Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec

Steffen Klassert says:

====================
1) Fix PMTU for IPv6 if the reported MTU minus the ESP overhead is
   smaller than 1280. From Jiri Bohac.

2) Fix xfrm interface ID and inter address family tunneling when
   migrating xfrm states. From Yan Yan.

3) Add missing xfrm intrerface ID initialization on xfrmi_changelink.
   From Antony Antony.

4) Enforce validity of xfrm offload input flags so that userspace can't
   send undefined flags to the offload driver.
   From Leon Romanovsky.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2022-02-25 10:44:15 +00:00
commit 31372fe966
11 changed files with 51 additions and 35 deletions

View File

@ -1568,7 +1568,6 @@ void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq); u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq);
int xfrm_init_replay(struct xfrm_state *x); int xfrm_init_replay(struct xfrm_state *x);
u32 __xfrm_state_mtu(struct xfrm_state *x, int mtu);
u32 xfrm_state_mtu(struct xfrm_state *x, int mtu); u32 xfrm_state_mtu(struct xfrm_state *x, int mtu);
int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload); int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload);
int xfrm_init_state(struct xfrm_state *x); int xfrm_init_state(struct xfrm_state *x);
@ -1681,14 +1680,15 @@ int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
const struct xfrm_migrate *m, int num_bundles, const struct xfrm_migrate *m, int num_bundles,
const struct xfrm_kmaddress *k, const struct xfrm_kmaddress *k,
const struct xfrm_encap_tmpl *encap); const struct xfrm_encap_tmpl *encap);
struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net); struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net,
u32 if_id);
struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x, struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
struct xfrm_migrate *m, struct xfrm_migrate *m,
struct xfrm_encap_tmpl *encap); struct xfrm_encap_tmpl *encap);
int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
struct xfrm_migrate *m, int num_bundles, struct xfrm_migrate *m, int num_bundles,
struct xfrm_kmaddress *k, struct net *net, struct xfrm_kmaddress *k, struct net *net,
struct xfrm_encap_tmpl *encap); struct xfrm_encap_tmpl *encap, u32 if_id);
#endif #endif
int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport); int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport);

View File

@ -511,6 +511,12 @@ struct xfrm_user_offload {
int ifindex; int ifindex;
__u8 flags; __u8 flags;
}; };
/* This flag was exposed without any kernel code that supporting it.
* Unfortunately, strongswan has the code that uses sets this flag,
* which makes impossible to reuse this bit.
*
* So leave it here to make sure that it won't be reused by mistake.
*/
#define XFRM_OFFLOAD_IPV6 1 #define XFRM_OFFLOAD_IPV6 1
#define XFRM_OFFLOAD_INBOUND 2 #define XFRM_OFFLOAD_INBOUND 2

View File

@ -671,7 +671,7 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb);
u32 padto; u32 padto;
padto = min(x->tfcpad, __xfrm_state_mtu(x, dst->child_mtu_cached)); padto = min(x->tfcpad, xfrm_state_mtu(x, dst->child_mtu_cached));
if (skb->len < padto) if (skb->len < padto)
esp.tfclen = padto - skb->len; esp.tfclen = padto - skb->len;
} }

View File

@ -707,7 +707,7 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb);
u32 padto; u32 padto;
padto = min(x->tfcpad, __xfrm_state_mtu(x, dst->child_mtu_cached)); padto = min(x->tfcpad, xfrm_state_mtu(x, dst->child_mtu_cached));
if (skb->len < padto) if (skb->len < padto)
esp.tfclen = padto - skb->len; esp.tfclen = padto - skb->len;
} }

View File

@ -1408,8 +1408,6 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork,
if (np->frag_size) if (np->frag_size)
mtu = np->frag_size; mtu = np->frag_size;
} }
if (mtu < IPV6_MIN_MTU)
return -EINVAL;
cork->base.fragsize = mtu; cork->base.fragsize = mtu;
cork->base.gso_size = ipc6->gso_size; cork->base.gso_size = ipc6->gso_size;
cork->base.tx_flags = 0; cork->base.tx_flags = 0;
@ -1471,8 +1469,6 @@ static int __ip6_append_data(struct sock *sk,
fragheaderlen = sizeof(struct ipv6hdr) + rt->rt6i_nfheader_len + fragheaderlen = sizeof(struct ipv6hdr) + rt->rt6i_nfheader_len +
(opt ? opt->opt_nflen : 0); (opt ? opt->opt_nflen : 0);
maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen -
sizeof(struct frag_hdr);
headersize = sizeof(struct ipv6hdr) + headersize = sizeof(struct ipv6hdr) +
(opt ? opt->opt_flen + opt->opt_nflen : 0) + (opt ? opt->opt_flen + opt->opt_nflen : 0) +
@ -1480,6 +1476,13 @@ static int __ip6_append_data(struct sock *sk,
sizeof(struct frag_hdr) : 0) + sizeof(struct frag_hdr) : 0) +
rt->rt6i_nfheader_len; rt->rt6i_nfheader_len;
if (mtu < fragheaderlen ||
((mtu - fragheaderlen) & ~7) + fragheaderlen < sizeof(struct frag_hdr))
goto emsgsize;
maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen -
sizeof(struct frag_hdr);
/* as per RFC 7112 section 5, the entire IPv6 Header Chain must fit /* as per RFC 7112 section 5, the entire IPv6 Header Chain must fit
* the first fragment * the first fragment
*/ */

View File

@ -2623,7 +2623,7 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,
} }
return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i, return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i,
kma ? &k : NULL, net, NULL); kma ? &k : NULL, net, NULL, 0);
out: out:
return err; return err;

View File

@ -223,6 +223,9 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
if (x->encap || x->tfcpad) if (x->encap || x->tfcpad)
return -EINVAL; return -EINVAL;
if (xuo->flags & ~(XFRM_OFFLOAD_IPV6 | XFRM_OFFLOAD_INBOUND))
return -EINVAL;
dev = dev_get_by_index(net, xuo->ifindex); dev = dev_get_by_index(net, xuo->ifindex);
if (!dev) { if (!dev) {
if (!(xuo->flags & XFRM_OFFLOAD_INBOUND)) { if (!(xuo->flags & XFRM_OFFLOAD_INBOUND)) {
@ -262,7 +265,8 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
netdev_tracker_alloc(dev, &xso->dev_tracker, GFP_ATOMIC); netdev_tracker_alloc(dev, &xso->dev_tracker, GFP_ATOMIC);
xso->real_dev = dev; xso->real_dev = dev;
xso->num_exthdrs = 1; xso->num_exthdrs = 1;
xso->flags = xuo->flags; /* Don't forward bit that is not implemented */
xso->flags = xuo->flags & ~XFRM_OFFLOAD_IPV6;
err = dev->xfrmdev_ops->xdo_dev_state_add(x); err = dev->xfrmdev_ops->xdo_dev_state_add(x);
if (err) { if (err) {

View File

@ -673,12 +673,12 @@ static int xfrmi_changelink(struct net_device *dev, struct nlattr *tb[],
struct net *net = xi->net; struct net *net = xi->net;
struct xfrm_if_parms p = {}; struct xfrm_if_parms p = {};
xfrmi_netlink_parms(data, &p);
if (!p.if_id) { if (!p.if_id) {
NL_SET_ERR_MSG(extack, "if_id must be non zero"); NL_SET_ERR_MSG(extack, "if_id must be non zero");
return -EINVAL; return -EINVAL;
} }
xfrmi_netlink_parms(data, &p);
xi = xfrmi_locate(net, &p); xi = xfrmi_locate(net, &p);
if (!xi) { if (!xi) {
xi = netdev_priv(dev); xi = netdev_priv(dev);

View File

@ -4256,7 +4256,7 @@ static bool xfrm_migrate_selector_match(const struct xfrm_selector *sel_cmp,
} }
static struct xfrm_policy *xfrm_migrate_policy_find(const struct xfrm_selector *sel, static struct xfrm_policy *xfrm_migrate_policy_find(const struct xfrm_selector *sel,
u8 dir, u8 type, struct net *net) u8 dir, u8 type, struct net *net, u32 if_id)
{ {
struct xfrm_policy *pol, *ret = NULL; struct xfrm_policy *pol, *ret = NULL;
struct hlist_head *chain; struct hlist_head *chain;
@ -4265,7 +4265,8 @@ static struct xfrm_policy *xfrm_migrate_policy_find(const struct xfrm_selector *
spin_lock_bh(&net->xfrm.xfrm_policy_lock); spin_lock_bh(&net->xfrm.xfrm_policy_lock);
chain = policy_hash_direct(net, &sel->daddr, &sel->saddr, sel->family, dir); chain = policy_hash_direct(net, &sel->daddr, &sel->saddr, sel->family, dir);
hlist_for_each_entry(pol, chain, bydst) { hlist_for_each_entry(pol, chain, bydst) {
if (xfrm_migrate_selector_match(sel, &pol->selector) && if ((if_id == 0 || pol->if_id == if_id) &&
xfrm_migrate_selector_match(sel, &pol->selector) &&
pol->type == type) { pol->type == type) {
ret = pol; ret = pol;
priority = ret->priority; priority = ret->priority;
@ -4277,7 +4278,8 @@ static struct xfrm_policy *xfrm_migrate_policy_find(const struct xfrm_selector *
if ((pol->priority >= priority) && ret) if ((pol->priority >= priority) && ret)
break; break;
if (xfrm_migrate_selector_match(sel, &pol->selector) && if ((if_id == 0 || pol->if_id == if_id) &&
xfrm_migrate_selector_match(sel, &pol->selector) &&
pol->type == type) { pol->type == type) {
ret = pol; ret = pol;
break; break;
@ -4393,7 +4395,7 @@ 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, int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
struct xfrm_migrate *m, int num_migrate, struct xfrm_migrate *m, int num_migrate,
struct xfrm_kmaddress *k, struct net *net, struct xfrm_kmaddress *k, struct net *net,
struct xfrm_encap_tmpl *encap) struct xfrm_encap_tmpl *encap, u32 if_id)
{ {
int i, err, nx_cur = 0, nx_new = 0; int i, err, nx_cur = 0, nx_new = 0;
struct xfrm_policy *pol = NULL; struct xfrm_policy *pol = NULL;
@ -4412,14 +4414,14 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
} }
/* Stage 1 - find policy */ /* Stage 1 - find policy */
if ((pol = xfrm_migrate_policy_find(sel, dir, type, net)) == NULL) { if ((pol = xfrm_migrate_policy_find(sel, dir, type, net, if_id)) == NULL) {
err = -ENOENT; err = -ENOENT;
goto out; goto out;
} }
/* Stage 2 - find and update state(s) */ /* Stage 2 - find and update state(s) */
for (i = 0, mp = m; i < num_migrate; i++, mp++) { for (i = 0, mp = m; i < num_migrate; i++, mp++) {
if ((x = xfrm_migrate_state_find(mp, net))) { if ((x = xfrm_migrate_state_find(mp, net, if_id))) {
x_cur[nx_cur] = x; x_cur[nx_cur] = x;
nx_cur++; nx_cur++;
xc = xfrm_state_migrate(x, mp, encap); xc = xfrm_state_migrate(x, mp, encap);

View File

@ -1579,9 +1579,6 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig,
memcpy(&x->mark, &orig->mark, sizeof(x->mark)); memcpy(&x->mark, &orig->mark, sizeof(x->mark));
memcpy(&x->props.smark, &orig->props.smark, sizeof(x->props.smark)); memcpy(&x->props.smark, &orig->props.smark, sizeof(x->props.smark));
if (xfrm_init_state(x) < 0)
goto error;
x->props.flags = orig->props.flags; x->props.flags = orig->props.flags;
x->props.extra_flags = orig->props.extra_flags; x->props.extra_flags = orig->props.extra_flags;
@ -1606,7 +1603,8 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig,
return NULL; return NULL;
} }
struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net) struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net,
u32 if_id)
{ {
unsigned int h; unsigned int h;
struct xfrm_state *x = NULL; struct xfrm_state *x = NULL;
@ -1622,6 +1620,8 @@ struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *n
continue; continue;
if (m->reqid && x->props.reqid != m->reqid) if (m->reqid && x->props.reqid != m->reqid)
continue; continue;
if (if_id != 0 && x->if_id != if_id)
continue;
if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr, if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr,
m->old_family) || m->old_family) ||
!xfrm_addr_equal(&x->props.saddr, &m->old_saddr, !xfrm_addr_equal(&x->props.saddr, &m->old_saddr,
@ -1637,6 +1637,8 @@ struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *n
if (x->props.mode != m->mode || if (x->props.mode != m->mode ||
x->id.proto != m->proto) x->id.proto != m->proto)
continue; continue;
if (if_id != 0 && x->if_id != if_id)
continue;
if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr, if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr,
m->old_family) || m->old_family) ||
!xfrm_addr_equal(&x->props.saddr, &m->old_saddr, !xfrm_addr_equal(&x->props.saddr, &m->old_saddr,
@ -1663,6 +1665,11 @@ struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
if (!xc) if (!xc)
return NULL; return NULL;
xc->props.family = m->new_family;
if (xfrm_init_state(xc) < 0)
goto error;
memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr)); memcpy(&xc->id.daddr, &m->new_daddr, sizeof(xc->id.daddr));
memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr)); memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr));
@ -2572,7 +2579,7 @@ void xfrm_state_delete_tunnel(struct xfrm_state *x)
} }
EXPORT_SYMBOL(xfrm_state_delete_tunnel); EXPORT_SYMBOL(xfrm_state_delete_tunnel);
u32 __xfrm_state_mtu(struct xfrm_state *x, int mtu) u32 xfrm_state_mtu(struct xfrm_state *x, int mtu)
{ {
const struct xfrm_type *type = READ_ONCE(x->type); const struct xfrm_type *type = READ_ONCE(x->type);
struct crypto_aead *aead; struct crypto_aead *aead;
@ -2603,17 +2610,7 @@ u32 __xfrm_state_mtu(struct xfrm_state *x, int mtu)
return ((mtu - x->props.header_len - crypto_aead_authsize(aead) - return ((mtu - x->props.header_len - crypto_aead_authsize(aead) -
net_adj) & ~(blksize - 1)) + net_adj - 2; net_adj) & ~(blksize - 1)) + net_adj - 2;
} }
EXPORT_SYMBOL_GPL(__xfrm_state_mtu); EXPORT_SYMBOL_GPL(xfrm_state_mtu);
u32 xfrm_state_mtu(struct xfrm_state *x, int mtu)
{
mtu = __xfrm_state_mtu(x, mtu);
if (x->props.family == AF_INET6 && mtu < IPV6_MIN_MTU)
return IPV6_MIN_MTU;
return mtu;
}
int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload) int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload)
{ {

View File

@ -2608,6 +2608,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
int n = 0; int n = 0;
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct xfrm_encap_tmpl *encap = NULL; struct xfrm_encap_tmpl *encap = NULL;
u32 if_id = 0;
if (attrs[XFRMA_MIGRATE] == NULL) if (attrs[XFRMA_MIGRATE] == NULL)
return -EINVAL; return -EINVAL;
@ -2632,7 +2633,10 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
return -ENOMEM; return -ENOMEM;
} }
err = xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp, net, encap); 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);
kfree(encap); kfree(encap);