Commit cc7e2f59 authored by David S. Miller's avatar David S. Miller

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

Steffen Klassert says:

====================
pull request (net): ipsec 2022-03-09

1) Fix IPv6 PMTU discovery for xfrm interfaces.
   From Lina Wang.

2) Revert failing for policies and states that are
   configured with XFRMA_IF_ID 0. It broke a
   user configuration. From Kai Lueke.

3) Fix a possible buffer overflow in the ESP output path.

4) Fix ESP GSO for tunnel and BEET mode on inter address
   family tunnels.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 71171ac8 23c7f8d7
...@@ -4602,6 +4602,8 @@ int skb_csum_hwoffload_help(struct sk_buff *skb, ...@@ -4602,6 +4602,8 @@ int skb_csum_hwoffload_help(struct sk_buff *skb,
struct sk_buff *__skb_gso_segment(struct sk_buff *skb, struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
netdev_features_t features, bool tx_path); netdev_features_t features, bool tx_path);
struct sk_buff *skb_eth_gso_segment(struct sk_buff *skb,
netdev_features_t features, __be16 type);
struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb, struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb,
netdev_features_t features); netdev_features_t features);
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#define ESP_SKB_FRAG_MAXSIZE (PAGE_SIZE << SKB_FRAG_PAGE_ORDER)
struct ip_esp_hdr; struct ip_esp_hdr;
static inline struct ip_esp_hdr *ip_esp_hdr(const struct sk_buff *skb) static inline struct ip_esp_hdr *ip_esp_hdr(const struct sk_buff *skb)
......
...@@ -92,6 +92,31 @@ void dev_remove_offload(struct packet_offload *po) ...@@ -92,6 +92,31 @@ void dev_remove_offload(struct packet_offload *po)
} }
EXPORT_SYMBOL(dev_remove_offload); EXPORT_SYMBOL(dev_remove_offload);
/**
* skb_eth_gso_segment - segmentation handler for ethernet protocols.
* @skb: buffer to segment
* @features: features for the output path (see dev->features)
* @type: Ethernet Protocol ID
*/
struct sk_buff *skb_eth_gso_segment(struct sk_buff *skb,
netdev_features_t features, __be16 type)
{
struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
struct packet_offload *ptype;
rcu_read_lock();
list_for_each_entry_rcu(ptype, &offload_base, list) {
if (ptype->type == type && ptype->callbacks.gso_segment) {
segs = ptype->callbacks.gso_segment(skb, features);
break;
}
}
rcu_read_unlock();
return segs;
}
EXPORT_SYMBOL(skb_eth_gso_segment);
/** /**
* skb_mac_gso_segment - mac layer segmentation handler. * skb_mac_gso_segment - mac layer segmentation handler.
* @skb: buffer to segment * @skb: buffer to segment
......
...@@ -446,6 +446,7 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * ...@@ -446,6 +446,7 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *
struct page *page; struct page *page;
struct sk_buff *trailer; struct sk_buff *trailer;
int tailen = esp->tailen; int tailen = esp->tailen;
unsigned int allocsz;
/* this is non-NULL only with TCP/UDP Encapsulation */ /* this is non-NULL only with TCP/UDP Encapsulation */
if (x->encap) { if (x->encap) {
...@@ -455,6 +456,10 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * ...@@ -455,6 +456,10 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *
return err; return err;
} }
allocsz = ALIGN(skb->data_len + tailen, L1_CACHE_BYTES);
if (allocsz > ESP_SKB_FRAG_MAXSIZE)
goto cow;
if (!skb_cloned(skb)) { if (!skb_cloned(skb)) {
if (tailen <= skb_tailroom(skb)) { if (tailen <= skb_tailroom(skb)) {
nfrags = 1; nfrags = 1;
......
...@@ -110,8 +110,7 @@ static struct sk_buff *xfrm4_tunnel_gso_segment(struct xfrm_state *x, ...@@ -110,8 +110,7 @@ static struct sk_buff *xfrm4_tunnel_gso_segment(struct xfrm_state *x,
struct sk_buff *skb, struct sk_buff *skb,
netdev_features_t features) netdev_features_t features)
{ {
__skb_push(skb, skb->mac_len); return skb_eth_gso_segment(skb, features, htons(ETH_P_IP));
return skb_mac_gso_segment(skb, features);
} }
static struct sk_buff *xfrm4_transport_gso_segment(struct xfrm_state *x, static struct sk_buff *xfrm4_transport_gso_segment(struct xfrm_state *x,
...@@ -160,6 +159,9 @@ static struct sk_buff *xfrm4_beet_gso_segment(struct xfrm_state *x, ...@@ -160,6 +159,9 @@ static struct sk_buff *xfrm4_beet_gso_segment(struct xfrm_state *x,
skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4; skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4;
} }
if (proto == IPPROTO_IPV6)
skb_shinfo(skb)->gso_type |= SKB_GSO_IPXIP4;
__skb_pull(skb, skb_transport_offset(skb)); __skb_pull(skb, skb_transport_offset(skb));
ops = rcu_dereference(inet_offloads[proto]); ops = rcu_dereference(inet_offloads[proto]);
if (likely(ops && ops->callbacks.gso_segment)) if (likely(ops && ops->callbacks.gso_segment))
......
...@@ -482,6 +482,7 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info ...@@ -482,6 +482,7 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info
struct page *page; struct page *page;
struct sk_buff *trailer; struct sk_buff *trailer;
int tailen = esp->tailen; int tailen = esp->tailen;
unsigned int allocsz;
if (x->encap) { if (x->encap) {
int err = esp6_output_encap(x, skb, esp); int err = esp6_output_encap(x, skb, esp);
...@@ -490,6 +491,10 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info ...@@ -490,6 +491,10 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info
return err; return err;
} }
allocsz = ALIGN(skb->data_len + tailen, L1_CACHE_BYTES);
if (allocsz > ESP_SKB_FRAG_MAXSIZE)
goto cow;
if (!skb_cloned(skb)) { if (!skb_cloned(skb)) {
if (tailen <= skb_tailroom(skb)) { if (tailen <= skb_tailroom(skb)) {
nfrags = 1; nfrags = 1;
......
...@@ -145,8 +145,7 @@ static struct sk_buff *xfrm6_tunnel_gso_segment(struct xfrm_state *x, ...@@ -145,8 +145,7 @@ static struct sk_buff *xfrm6_tunnel_gso_segment(struct xfrm_state *x,
struct sk_buff *skb, struct sk_buff *skb,
netdev_features_t features) netdev_features_t features)
{ {
__skb_push(skb, skb->mac_len); return skb_eth_gso_segment(skb, features, htons(ETH_P_IPV6));
return skb_mac_gso_segment(skb, features);
} }
static struct sk_buff *xfrm6_transport_gso_segment(struct xfrm_state *x, static struct sk_buff *xfrm6_transport_gso_segment(struct xfrm_state *x,
...@@ -199,6 +198,9 @@ static struct sk_buff *xfrm6_beet_gso_segment(struct xfrm_state *x, ...@@ -199,6 +198,9 @@ static struct sk_buff *xfrm6_beet_gso_segment(struct xfrm_state *x,
ipv6_skip_exthdr(skb, 0, &proto, &frag); ipv6_skip_exthdr(skb, 0, &proto, &frag);
} }
if (proto == IPPROTO_IPIP)
skb_shinfo(skb)->gso_type |= SKB_GSO_IPXIP6;
__skb_pull(skb, skb_transport_offset(skb)); __skb_pull(skb, skb_transport_offset(skb));
ops = rcu_dereference(inet6_offloads[proto]); ops = rcu_dereference(inet6_offloads[proto]);
if (likely(ops && ops->callbacks.gso_segment)) if (likely(ops && ops->callbacks.gso_segment))
......
...@@ -45,6 +45,19 @@ static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buf ...@@ -45,6 +45,19 @@ static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buf
return xfrm_output(sk, skb); return xfrm_output(sk, skb);
} }
static int xfrm6_noneed_fragment(struct sk_buff *skb)
{
struct frag_hdr *fh;
u8 prevhdr = ipv6_hdr(skb)->nexthdr;
if (prevhdr != NEXTHDR_FRAGMENT)
return 0;
fh = (struct frag_hdr *)(skb->data + sizeof(struct ipv6hdr));
if (fh->nexthdr == NEXTHDR_ESP || fh->nexthdr == NEXTHDR_AUTH)
return 1;
return 0;
}
static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{ {
struct dst_entry *dst = skb_dst(skb); struct dst_entry *dst = skb_dst(skb);
...@@ -73,6 +86,9 @@ static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) ...@@ -73,6 +86,9 @@ static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
xfrm6_local_rxpmtu(skb, mtu); xfrm6_local_rxpmtu(skb, mtu);
kfree_skb(skb); kfree_skb(skb);
return -EMSGSIZE; return -EMSGSIZE;
} else if (toobig && xfrm6_noneed_fragment(skb)) {
skb->ignore_df = 1;
goto skip_frag;
} else if (!skb->ignore_df && toobig && skb->sk) { } else if (!skb->ignore_df && toobig && skb->sk) {
xfrm_local_error(skb, mtu); xfrm_local_error(skb, mtu);
kfree_skb(skb); kfree_skb(skb);
......
...@@ -304,7 +304,10 @@ xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) ...@@ -304,7 +304,10 @@ xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
if (mtu < IPV6_MIN_MTU) if (mtu < IPV6_MIN_MTU)
mtu = IPV6_MIN_MTU; mtu = IPV6_MIN_MTU;
icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); if (skb->len > 1280)
icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
else
goto xmit;
} else { } else {
if (!(ip_hdr(skb)->frag_off & htons(IP_DF))) if (!(ip_hdr(skb)->frag_off & htons(IP_DF)))
goto xmit; goto xmit;
......
...@@ -630,13 +630,8 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, ...@@ -630,13 +630,8 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
xfrm_smark_init(attrs, &x->props.smark); xfrm_smark_init(attrs, &x->props.smark);
if (attrs[XFRMA_IF_ID]) { if (attrs[XFRMA_IF_ID])
x->if_id = nla_get_u32(attrs[XFRMA_IF_ID]); x->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
if (!x->if_id) {
err = -EINVAL;
goto error;
}
}
err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV]); err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV]);
if (err) if (err)
...@@ -1432,13 +1427,8 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -1432,13 +1427,8 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
mark = xfrm_mark_get(attrs, &m); mark = xfrm_mark_get(attrs, &m);
if (attrs[XFRMA_IF_ID]) { if (attrs[XFRMA_IF_ID])
if_id = nla_get_u32(attrs[XFRMA_IF_ID]); if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
if (!if_id) {
err = -EINVAL;
goto out_noput;
}
}
if (p->info.seq) { if (p->info.seq) {
x = xfrm_find_acq_byseq(net, mark, p->info.seq); x = xfrm_find_acq_byseq(net, mark, p->info.seq);
...@@ -1751,13 +1741,8 @@ static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_us ...@@ -1751,13 +1741,8 @@ static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_us
xfrm_mark_get(attrs, &xp->mark); xfrm_mark_get(attrs, &xp->mark);
if (attrs[XFRMA_IF_ID]) { if (attrs[XFRMA_IF_ID])
xp->if_id = nla_get_u32(attrs[XFRMA_IF_ID]); xp->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
if (!xp->if_id) {
err = -EINVAL;
goto error;
}
}
return xp; return xp;
error: error:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment