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. ====================
Unified · Split
| @@ -4602,6 +4602,8 @@ int skb_csum_hwoffload_help(struct sk_buff *skb, · 4601 unmodified lines | ||
| 4602 | 4602 | |
| 4603 | 4603 | struct sk_buff *__skb_gso_segment(struct sk_buff *skb, |
| 4604 | 4604 | netdev_features_t features, bool tx_path); |
| 4605 | +struct sk_buff *skb_eth_gso_segment(struct sk_buff *skb, | |
| 4606 | + netdev_features_t features, __be16 type); | |
| 4605 | 4607 | struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb, |
| 4606 | 4608 | netdev_features_t features); |
| 4607 | 4609 | |
| @@ -4,6 +4,8 @@ · 3 unmodified lines | ||
| 4 | 4 | |
| 5 | 5 | #include <linux/skbuff.h> |
| 6 | 6 | |
| 7 | +#define ESP_SKB_FRAG_MAXSIZE (PAGE_SIZE << SKB_FRAG_PAGE_ORDER) | |
| 8 | + | |
| 7 | 9 | struct ip_esp_hdr; |
| 8 | 10 | |
| 9 | 11 | 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) · 91 unmodified lines | ||
| 92 | 92 | } |
| 93 | 93 | EXPORT_SYMBOL(dev_remove_offload); |
| 94 | 94 | |
| 95 | +/** | |
| 96 | + * skb_eth_gso_segment - segmentation handler for ethernet protocols. | |
| 97 | + * @skb: buffer to segment | |
| 98 | + * @features: features for the output path (see dev->features) | |
| 99 | + * @type: Ethernet Protocol ID | |
| 100 | + */ | |
| 101 | +struct sk_buff *skb_eth_gso_segment(struct sk_buff *skb, | |
| 102 | + netdev_features_t features, __be16 type) | |
| 103 | +{ | |
| 104 | + struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); | |
| 105 | + struct packet_offload *ptype; | |
| 106 | + | |
| 107 | + rcu_read_lock(); | |
| 108 | + list_for_each_entry_rcu(ptype, &offload_base, list) { | |
| 109 | + if (ptype->type == type && ptype->callbacks.gso_segment) { | |
| 110 | + segs = ptype->callbacks.gso_segment(skb, features); | |
| 111 | + break; | |
| 112 | + } | |
| 113 | + } | |
| 114 | + rcu_read_unlock(); | |
| 115 | + | |
| 116 | + return segs; | |
| 117 | +} | |
| 118 | +EXPORT_SYMBOL(skb_eth_gso_segment); | |
| 119 | + | |
| 95 | 120 | /** |
| 96 | 121 | * skb_mac_gso_segment - mac layer segmentation handler. |
| 97 | 122 | * @skb: buffer to segment |
| @@ -446,6 +446,7 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * · 445 unmodified lines | ||
| 446 | 446 | struct page *page; |
| 447 | 447 | struct sk_buff *trailer; |
| 448 | 448 | int tailen = esp->tailen; |
| 449 | + unsigned int allocsz; | |
| 449 | 450 | |
| 450 | 451 | /* this is non-NULL only with TCP/UDP Encapsulation */ |
| 451 | 452 | if (x->encap) { |
| @@ -455,6 +456,10 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * · 3 unmodified lines | ||
| 455 | 456 | return err; |
| 456 | 457 | } |
| 457 | 458 | |
| 459 | + allocsz = ALIGN(skb->data_len + tailen, L1_CACHE_BYTES); | |
| 460 | + if (allocsz > ESP_SKB_FRAG_MAXSIZE) | |
| 461 | + goto cow; | |
| 462 | + | |
| 458 | 463 | if (!skb_cloned(skb)) { |
| 459 | 464 | if (tailen <= skb_tailroom(skb)) { |
| 460 | 465 | nfrags = 1; |
| @@ -110,8 +110,7 @@ static struct sk_buff *xfrm4_tunnel_gso_segment(struct xfrm_state *x, · 109 unmodified lines | ||
| 110 | 110 | struct sk_buff *skb, |
| 111 | 111 | netdev_features_t features) |
| 112 | 112 | { |
| 113 | − __skb_push(skb, skb->mac_len); | |
| 114 | − return skb_mac_gso_segment(skb, features); | |
| 113 | + return skb_eth_gso_segment(skb, features, htons(ETH_P_IP)); | |
| 115 | 114 | } |
| 116 | 115 | |
| 117 | 116 | 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, · 42 unmodified lines | ||
| 160 | 159 | skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4; |
| 161 | 160 | } |
| 162 | 161 | |
| 162 | + if (proto == IPPROTO_IPV6) | |
| 163 | + skb_shinfo(skb)->gso_type |= SKB_GSO_IPXIP4; | |
| 164 | + | |
| 163 | 165 | __skb_pull(skb, skb_transport_offset(skb)); |
| 164 | 166 | ops = rcu_dereference(inet_offloads[proto]); |
| 165 | 167 | 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 · 481 unmodified lines | ||
| 482 | 482 | struct page *page; |
| 483 | 483 | struct sk_buff *trailer; |
| 484 | 484 | int tailen = esp->tailen; |
| 485 | + unsigned int allocsz; | |
| 485 | 486 | |
| 486 | 487 | if (x->encap) { |
| 487 | 488 | 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 · 2 unmodified lines | ||
| 490 | 491 | return err; |
| 491 | 492 | } |
| 492 | 493 | |
| 494 | + allocsz = ALIGN(skb->data_len + tailen, L1_CACHE_BYTES); | |
| 495 | + if (allocsz > ESP_SKB_FRAG_MAXSIZE) | |
| 496 | + goto cow; | |
| 497 | + | |
| 493 | 498 | if (!skb_cloned(skb)) { |
| 494 | 499 | if (tailen <= skb_tailroom(skb)) { |
| 495 | 500 | nfrags = 1; |
| @@ -145,8 +145,7 @@ static struct sk_buff *xfrm6_tunnel_gso_segment(struct xfrm_state *x, · 144 unmodified lines | ||
| 145 | 145 | struct sk_buff *skb, |
| 146 | 146 | netdev_features_t features) |
| 147 | 147 | { |
| 148 | − __skb_push(skb, skb->mac_len); | |
| 149 | − return skb_mac_gso_segment(skb, features); | |
| 148 | + return skb_eth_gso_segment(skb, features, htons(ETH_P_IPV6)); | |
| 150 | 149 | } |
| 151 | 150 | |
| 152 | 151 | 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, · 46 unmodified lines | ||
| 199 | 198 | ipv6_skip_exthdr(skb, 0, &proto, &frag); |
| 200 | 199 | } |
| 201 | 200 | |
| 201 | + if (proto == IPPROTO_IPIP) | |
| 202 | + skb_shinfo(skb)->gso_type |= SKB_GSO_IPXIP6; | |
| 203 | + | |
| 202 | 204 | __skb_pull(skb, skb_transport_offset(skb)); |
| 203 | 205 | ops = rcu_dereference(inet6_offloads[proto]); |
| 204 | 206 | 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 · 44 unmodified lines | ||
| 45 | 45 | return xfrm_output(sk, skb); |
| 46 | 46 | } |
| 47 | 47 | |
| 48 | +static int xfrm6_noneed_fragment(struct sk_buff *skb) | |
| 49 | +{ | |
| 50 | + struct frag_hdr *fh; | |
| 51 | + u8 prevhdr = ipv6_hdr(skb)->nexthdr; | |
| 52 | + | |
| 53 | + if (prevhdr != NEXTHDR_FRAGMENT) | |
| 54 | + return 0; | |
| 55 | + fh = (struct frag_hdr *)(skb->data + sizeof(struct ipv6hdr)); | |
| 56 | + if (fh->nexthdr == NEXTHDR_ESP || fh->nexthdr == NEXTHDR_AUTH) | |
| 57 | + return 1; | |
| 58 | + return 0; | |
| 59 | +} | |
| 60 | + | |
| 48 | 61 | static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb) |
| 49 | 62 | { |
| 50 | 63 | 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) · 22 unmodified lines | ||
| 73 | 86 | xfrm6_local_rxpmtu(skb, mtu); |
| 74 | 87 | kfree_skb(skb); |
| 75 | 88 | return -EMSGSIZE; |
| 89 | + } else if (toobig && xfrm6_noneed_fragment(skb)) { | |
| 90 | + skb->ignore_df = 1; | |
| 91 | + goto skip_frag; | |
| 76 | 92 | } else if (!skb->ignore_df && toobig && skb->sk) { |
| 77 | 93 | xfrm_local_error(skb, mtu); |
| 78 | 94 | kfree_skb(skb); |
| @@ -304,7 +304,10 @@ xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) · 303 unmodified lines | ||
| 304 | 304 | if (mtu < IPV6_MIN_MTU) |
| 305 | 305 | mtu = IPV6_MIN_MTU; |
| 306 | 306 | |
| 307 | − icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); | |
| 307 | + if (skb->len > 1280) | |
| 308 | + icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); | |
| 309 | + else | |
| 310 | + goto xmit; | |
| 308 | 311 | } else { |
| 309 | 312 | if (!(ip_hdr(skb)->frag_off & htons(IP_DF))) |
| 310 | 313 | goto xmit; |
| @@ -630,13 +630,8 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, · 629 unmodified lines | ||
| 630 | 630 | |
| 631 | 631 | xfrm_smark_init(attrs, &x->props.smark); |
| 632 | 632 | |
| 633 | − if (attrs[XFRMA_IF_ID]) { | |
| 633 | + if (attrs[XFRMA_IF_ID]) | |
| 634 | 634 | x->if_id = nla_get_u32(attrs[XFRMA_IF_ID]); |
| 635 | − if (!x->if_id) { | |
| 636 | − err = -EINVAL; | |
| 637 | − goto error; | |
| 638 | − } | |
| 639 | − } | |
| 640 | 635 | |
| 641 | 636 | err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV]); |
| 642 | 637 | if (err) |
| @@ -1432,13 +1427,8 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, · 789 unmodified lines | ||
| 1432 | 1427 | |
| 1433 | 1428 | mark = xfrm_mark_get(attrs, &m); |
| 1434 | 1429 | |
| 1435 | − if (attrs[XFRMA_IF_ID]) { | |
| 1430 | + if (attrs[XFRMA_IF_ID]) | |
| 1436 | 1431 | if_id = nla_get_u32(attrs[XFRMA_IF_ID]); |
| 1437 | − if (!if_id) { | |
| 1438 | − err = -EINVAL; | |
| 1439 | − goto out_noput; | |
| 1440 | − } | |
| 1441 | − } | |
| 1442 | 1432 | |
| 1443 | 1433 | if (p->info.seq) { |
| 1444 | 1434 | 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 · 306 unmodified lines | ||
| 1751 | 1741 | |
| 1752 | 1742 | xfrm_mark_get(attrs, &xp->mark); |
| 1753 | 1743 | |
| 1754 | − if (attrs[XFRMA_IF_ID]) { | |
| 1744 | + if (attrs[XFRMA_IF_ID]) | |
| 1755 | 1745 | xp->if_id = nla_get_u32(attrs[XFRMA_IF_ID]); |
| 1756 | − if (!xp->if_id) { | |
| 1757 | − err = -EINVAL; | |
| 1758 | − goto error; | |
| 1759 | − } | |
| 1760 | − } | |
| 1761 | 1746 | |
| 1762 | 1747 | return xp; |
| 1763 | 1748 | error: |