18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * xfrm_output.c - Common IPsec encapsulation code.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/errno.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
118c2ecf20Sopenharmony_ci#include <linux/netfilter.h>
128c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
158c2ecf20Sopenharmony_ci#include <net/dst.h>
168c2ecf20Sopenharmony_ci#include <net/icmp.h>
178c2ecf20Sopenharmony_ci#include <net/inet_ecn.h>
188c2ecf20Sopenharmony_ci#include <net/xfrm.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
218c2ecf20Sopenharmony_ci#include <net/ip6_route.h>
228c2ecf20Sopenharmony_ci#include <net/ipv6_stubs.h>
238c2ecf20Sopenharmony_ci#endif
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "xfrm_inout.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int xfrm_output2(struct net *net, struct sock *sk, struct sk_buff *skb);
288c2ecf20Sopenharmony_cistatic int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic int xfrm_skb_check_space(struct sk_buff *skb)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
338c2ecf20Sopenharmony_ci	int nhead = dst->header_len + LL_RESERVED_SPACE(dst->dev)
348c2ecf20Sopenharmony_ci		- skb_headroom(skb);
358c2ecf20Sopenharmony_ci	int ntail = dst->dev->needed_tailroom - skb_tailroom(skb);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	if (nhead <= 0) {
388c2ecf20Sopenharmony_ci		if (ntail <= 0)
398c2ecf20Sopenharmony_ci			return 0;
408c2ecf20Sopenharmony_ci		nhead = 0;
418c2ecf20Sopenharmony_ci	} else if (ntail < 0)
428c2ecf20Sopenharmony_ci		ntail = 0;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	return pskb_expand_head(skb, nhead, ntail, GFP_ATOMIC);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Children define the path of the packet through the
488c2ecf20Sopenharmony_ci * Linux networking.  Thus, destinations are stackable.
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic struct dst_entry *skb_dst_pop(struct sk_buff *skb)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct dst_entry *child = dst_clone(xfrm_dst_child(skb_dst(skb)));
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	skb_dst_drop(skb);
568c2ecf20Sopenharmony_ci	return child;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/* Add encapsulation header.
608c2ecf20Sopenharmony_ci *
618c2ecf20Sopenharmony_ci * The IP header will be moved forward to make space for the encapsulation
628c2ecf20Sopenharmony_ci * header.
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_cistatic int xfrm4_transport_output(struct xfrm_state *x, struct sk_buff *skb)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	struct iphdr *iph = ip_hdr(skb);
678c2ecf20Sopenharmony_ci	int ihl = iph->ihl * 4;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	skb_set_inner_transport_header(skb, skb_transport_offset(skb));
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	skb_set_network_header(skb, -x->props.header_len);
728c2ecf20Sopenharmony_ci	skb->mac_header = skb->network_header +
738c2ecf20Sopenharmony_ci			  offsetof(struct iphdr, protocol);
748c2ecf20Sopenharmony_ci	skb->transport_header = skb->network_header + ihl;
758c2ecf20Sopenharmony_ci	__skb_pull(skb, ihl);
768c2ecf20Sopenharmony_ci	memmove(skb_network_header(skb), iph, ihl);
778c2ecf20Sopenharmony_ci	return 0;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/* Add encapsulation header.
818c2ecf20Sopenharmony_ci *
828c2ecf20Sopenharmony_ci * The IP header and mutable extension headers will be moved forward to make
838c2ecf20Sopenharmony_ci * space for the encapsulation header.
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_cistatic int xfrm6_transport_output(struct xfrm_state *x, struct sk_buff *skb)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
888c2ecf20Sopenharmony_ci	struct ipv6hdr *iph;
898c2ecf20Sopenharmony_ci	u8 *prevhdr;
908c2ecf20Sopenharmony_ci	int hdr_len;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	iph = ipv6_hdr(skb);
938c2ecf20Sopenharmony_ci	skb_set_inner_transport_header(skb, skb_transport_offset(skb));
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	hdr_len = x->type->hdr_offset(x, skb, &prevhdr);
968c2ecf20Sopenharmony_ci	if (hdr_len < 0)
978c2ecf20Sopenharmony_ci		return hdr_len;
988c2ecf20Sopenharmony_ci	skb_set_mac_header(skb,
998c2ecf20Sopenharmony_ci			   (prevhdr - x->props.header_len) - skb->data);
1008c2ecf20Sopenharmony_ci	skb_set_network_header(skb, -x->props.header_len);
1018c2ecf20Sopenharmony_ci	skb->transport_header = skb->network_header + hdr_len;
1028c2ecf20Sopenharmony_ci	__skb_pull(skb, hdr_len);
1038c2ecf20Sopenharmony_ci	memmove(ipv6_hdr(skb), iph, hdr_len);
1048c2ecf20Sopenharmony_ci	return 0;
1058c2ecf20Sopenharmony_ci#else
1068c2ecf20Sopenharmony_ci	WARN_ON_ONCE(1);
1078c2ecf20Sopenharmony_ci	return -EAFNOSUPPORT;
1088c2ecf20Sopenharmony_ci#endif
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/* Add route optimization header space.
1128c2ecf20Sopenharmony_ci *
1138c2ecf20Sopenharmony_ci * The IP header and mutable extension headers will be moved forward to make
1148c2ecf20Sopenharmony_ci * space for the route optimization header.
1158c2ecf20Sopenharmony_ci */
1168c2ecf20Sopenharmony_cistatic int xfrm6_ro_output(struct xfrm_state *x, struct sk_buff *skb)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
1198c2ecf20Sopenharmony_ci	struct ipv6hdr *iph;
1208c2ecf20Sopenharmony_ci	u8 *prevhdr;
1218c2ecf20Sopenharmony_ci	int hdr_len;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	iph = ipv6_hdr(skb);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	hdr_len = x->type->hdr_offset(x, skb, &prevhdr);
1268c2ecf20Sopenharmony_ci	if (hdr_len < 0)
1278c2ecf20Sopenharmony_ci		return hdr_len;
1288c2ecf20Sopenharmony_ci	skb_set_mac_header(skb,
1298c2ecf20Sopenharmony_ci			   (prevhdr - x->props.header_len) - skb->data);
1308c2ecf20Sopenharmony_ci	skb_set_network_header(skb, -x->props.header_len);
1318c2ecf20Sopenharmony_ci	skb->transport_header = skb->network_header + hdr_len;
1328c2ecf20Sopenharmony_ci	__skb_pull(skb, hdr_len);
1338c2ecf20Sopenharmony_ci	memmove(ipv6_hdr(skb), iph, hdr_len);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	x->lastused = ktime_get_real_seconds();
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	return 0;
1388c2ecf20Sopenharmony_ci#else
1398c2ecf20Sopenharmony_ci	WARN_ON_ONCE(1);
1408c2ecf20Sopenharmony_ci	return -EAFNOSUPPORT;
1418c2ecf20Sopenharmony_ci#endif
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci/* Add encapsulation header.
1458c2ecf20Sopenharmony_ci *
1468c2ecf20Sopenharmony_ci * The top IP header will be constructed per draft-nikander-esp-beet-mode-06.txt.
1478c2ecf20Sopenharmony_ci */
1488c2ecf20Sopenharmony_cistatic int xfrm4_beet_encap_add(struct xfrm_state *x, struct sk_buff *skb)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct ip_beet_phdr *ph;
1518c2ecf20Sopenharmony_ci	struct iphdr *top_iph;
1528c2ecf20Sopenharmony_ci	int hdrlen, optlen;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	hdrlen = 0;
1558c2ecf20Sopenharmony_ci	optlen = XFRM_MODE_SKB_CB(skb)->optlen;
1568c2ecf20Sopenharmony_ci	if (unlikely(optlen))
1578c2ecf20Sopenharmony_ci		hdrlen += IPV4_BEET_PHMAXLEN - (optlen & 4);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	skb_set_network_header(skb, -x->props.header_len - hdrlen +
1608c2ecf20Sopenharmony_ci			       (XFRM_MODE_SKB_CB(skb)->ihl - sizeof(*top_iph)));
1618c2ecf20Sopenharmony_ci	if (x->sel.family != AF_INET6)
1628c2ecf20Sopenharmony_ci		skb->network_header += IPV4_BEET_PHMAXLEN;
1638c2ecf20Sopenharmony_ci	skb->mac_header = skb->network_header +
1648c2ecf20Sopenharmony_ci			  offsetof(struct iphdr, protocol);
1658c2ecf20Sopenharmony_ci	skb->transport_header = skb->network_header + sizeof(*top_iph);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	xfrm4_beet_make_header(skb);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	ph = __skb_pull(skb, XFRM_MODE_SKB_CB(skb)->ihl - hdrlen);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	top_iph = ip_hdr(skb);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (unlikely(optlen)) {
1748c2ecf20Sopenharmony_ci		if (WARN_ON(optlen < 0))
1758c2ecf20Sopenharmony_ci			return -EINVAL;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		ph->padlen = 4 - (optlen & 4);
1788c2ecf20Sopenharmony_ci		ph->hdrlen = optlen / 8;
1798c2ecf20Sopenharmony_ci		ph->nexthdr = top_iph->protocol;
1808c2ecf20Sopenharmony_ci		if (ph->padlen)
1818c2ecf20Sopenharmony_ci			memset(ph + 1, IPOPT_NOP, ph->padlen);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci		top_iph->protocol = IPPROTO_BEETPH;
1848c2ecf20Sopenharmony_ci		top_iph->ihl = sizeof(struct iphdr) / 4;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	top_iph->saddr = x->props.saddr.a4;
1888c2ecf20Sopenharmony_ci	top_iph->daddr = x->id.daddr.a4;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	return 0;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci/* Add encapsulation header.
1948c2ecf20Sopenharmony_ci *
1958c2ecf20Sopenharmony_ci * The top IP header will be constructed per RFC 2401.
1968c2ecf20Sopenharmony_ci */
1978c2ecf20Sopenharmony_cistatic int xfrm4_tunnel_encap_add(struct xfrm_state *x, struct sk_buff *skb)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
2008c2ecf20Sopenharmony_ci	struct iphdr *top_iph;
2018c2ecf20Sopenharmony_ci	int flags;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	skb_set_inner_network_header(skb, skb_network_offset(skb));
2048c2ecf20Sopenharmony_ci	skb_set_inner_transport_header(skb, skb_transport_offset(skb));
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	skb_set_network_header(skb, -x->props.header_len);
2078c2ecf20Sopenharmony_ci	skb->mac_header = skb->network_header +
2088c2ecf20Sopenharmony_ci			  offsetof(struct iphdr, protocol);
2098c2ecf20Sopenharmony_ci	skb->transport_header = skb->network_header + sizeof(*top_iph);
2108c2ecf20Sopenharmony_ci	top_iph = ip_hdr(skb);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	top_iph->ihl = 5;
2138c2ecf20Sopenharmony_ci	top_iph->version = 4;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	top_iph->protocol = xfrm_af2proto(skb_dst(skb)->ops->family);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/* DS disclosing depends on XFRM_SA_XFLAG_DONT_ENCAP_DSCP */
2188c2ecf20Sopenharmony_ci	if (x->props.extra_flags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP)
2198c2ecf20Sopenharmony_ci		top_iph->tos = 0;
2208c2ecf20Sopenharmony_ci	else
2218c2ecf20Sopenharmony_ci		top_iph->tos = XFRM_MODE_SKB_CB(skb)->tos;
2228c2ecf20Sopenharmony_ci	top_iph->tos = INET_ECN_encapsulate(top_iph->tos,
2238c2ecf20Sopenharmony_ci					    XFRM_MODE_SKB_CB(skb)->tos);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	flags = x->props.flags;
2268c2ecf20Sopenharmony_ci	if (flags & XFRM_STATE_NOECN)
2278c2ecf20Sopenharmony_ci		IP_ECN_clear(top_iph);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ?
2308c2ecf20Sopenharmony_ci		0 : (XFRM_MODE_SKB_CB(skb)->frag_off & htons(IP_DF));
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	top_iph->ttl = ip4_dst_hoplimit(xfrm_dst_child(dst));
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	top_iph->saddr = x->props.saddr.a4;
2358c2ecf20Sopenharmony_ci	top_iph->daddr = x->id.daddr.a4;
2368c2ecf20Sopenharmony_ci	ip_select_ident(dev_net(dst->dev), skb, NULL);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return 0;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
2428c2ecf20Sopenharmony_cistatic int xfrm6_tunnel_encap_add(struct xfrm_state *x, struct sk_buff *skb)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
2458c2ecf20Sopenharmony_ci	struct ipv6hdr *top_iph;
2468c2ecf20Sopenharmony_ci	int dsfield;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	skb_set_inner_network_header(skb, skb_network_offset(skb));
2498c2ecf20Sopenharmony_ci	skb_set_inner_transport_header(skb, skb_transport_offset(skb));
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	skb_set_network_header(skb, -x->props.header_len);
2528c2ecf20Sopenharmony_ci	skb->mac_header = skb->network_header +
2538c2ecf20Sopenharmony_ci			  offsetof(struct ipv6hdr, nexthdr);
2548c2ecf20Sopenharmony_ci	skb->transport_header = skb->network_header + sizeof(*top_iph);
2558c2ecf20Sopenharmony_ci	top_iph = ipv6_hdr(skb);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	top_iph->version = 6;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	memcpy(top_iph->flow_lbl, XFRM_MODE_SKB_CB(skb)->flow_lbl,
2608c2ecf20Sopenharmony_ci	       sizeof(top_iph->flow_lbl));
2618c2ecf20Sopenharmony_ci	top_iph->nexthdr = xfrm_af2proto(skb_dst(skb)->ops->family);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	if (x->props.extra_flags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP)
2648c2ecf20Sopenharmony_ci		dsfield = 0;
2658c2ecf20Sopenharmony_ci	else
2668c2ecf20Sopenharmony_ci		dsfield = XFRM_MODE_SKB_CB(skb)->tos;
2678c2ecf20Sopenharmony_ci	dsfield = INET_ECN_encapsulate(dsfield, XFRM_MODE_SKB_CB(skb)->tos);
2688c2ecf20Sopenharmony_ci	if (x->props.flags & XFRM_STATE_NOECN)
2698c2ecf20Sopenharmony_ci		dsfield &= ~INET_ECN_MASK;
2708c2ecf20Sopenharmony_ci	ipv6_change_dsfield(top_iph, 0, dsfield);
2718c2ecf20Sopenharmony_ci	top_iph->hop_limit = ip6_dst_hoplimit(xfrm_dst_child(dst));
2728c2ecf20Sopenharmony_ci	top_iph->saddr = *(struct in6_addr *)&x->props.saddr;
2738c2ecf20Sopenharmony_ci	top_iph->daddr = *(struct in6_addr *)&x->id.daddr;
2748c2ecf20Sopenharmony_ci	return 0;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic int xfrm6_beet_encap_add(struct xfrm_state *x, struct sk_buff *skb)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	struct ipv6hdr *top_iph;
2808c2ecf20Sopenharmony_ci	struct ip_beet_phdr *ph;
2818c2ecf20Sopenharmony_ci	int optlen, hdr_len;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	hdr_len = 0;
2848c2ecf20Sopenharmony_ci	optlen = XFRM_MODE_SKB_CB(skb)->optlen;
2858c2ecf20Sopenharmony_ci	if (unlikely(optlen))
2868c2ecf20Sopenharmony_ci		hdr_len += IPV4_BEET_PHMAXLEN - (optlen & 4);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	skb_set_network_header(skb, -x->props.header_len - hdr_len);
2898c2ecf20Sopenharmony_ci	if (x->sel.family != AF_INET6)
2908c2ecf20Sopenharmony_ci		skb->network_header += IPV4_BEET_PHMAXLEN;
2918c2ecf20Sopenharmony_ci	skb->mac_header = skb->network_header +
2928c2ecf20Sopenharmony_ci			  offsetof(struct ipv6hdr, nexthdr);
2938c2ecf20Sopenharmony_ci	skb->transport_header = skb->network_header + sizeof(*top_iph);
2948c2ecf20Sopenharmony_ci	ph = __skb_pull(skb, XFRM_MODE_SKB_CB(skb)->ihl - hdr_len);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	xfrm6_beet_make_header(skb);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	top_iph = ipv6_hdr(skb);
2998c2ecf20Sopenharmony_ci	if (unlikely(optlen)) {
3008c2ecf20Sopenharmony_ci		if (WARN_ON(optlen < 0))
3018c2ecf20Sopenharmony_ci			return -EINVAL;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		ph->padlen = 4 - (optlen & 4);
3048c2ecf20Sopenharmony_ci		ph->hdrlen = optlen / 8;
3058c2ecf20Sopenharmony_ci		ph->nexthdr = top_iph->nexthdr;
3068c2ecf20Sopenharmony_ci		if (ph->padlen)
3078c2ecf20Sopenharmony_ci			memset(ph + 1, IPOPT_NOP, ph->padlen);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci		top_iph->nexthdr = IPPROTO_BEETPH;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	top_iph->saddr = *(struct in6_addr *)&x->props.saddr;
3138c2ecf20Sopenharmony_ci	top_iph->daddr = *(struct in6_addr *)&x->id.daddr;
3148c2ecf20Sopenharmony_ci	return 0;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci#endif
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci/* Add encapsulation header.
3198c2ecf20Sopenharmony_ci *
3208c2ecf20Sopenharmony_ci * On exit, the transport header will be set to the start of the
3218c2ecf20Sopenharmony_ci * encapsulation header to be filled in by x->type->output and the mac
3228c2ecf20Sopenharmony_ci * header will be set to the nextheader (protocol for IPv4) field of the
3238c2ecf20Sopenharmony_ci * extension header directly preceding the encapsulation header, or in
3248c2ecf20Sopenharmony_ci * its absence, that of the top IP header.
3258c2ecf20Sopenharmony_ci * The value of the network header will always point to the top IP header
3268c2ecf20Sopenharmony_ci * while skb->data will point to the payload.
3278c2ecf20Sopenharmony_ci */
3288c2ecf20Sopenharmony_cistatic int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	int err;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	err = xfrm_inner_extract_output(x, skb);
3338c2ecf20Sopenharmony_ci	if (err)
3348c2ecf20Sopenharmony_ci		return err;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE;
3378c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_IP);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	switch (x->outer_mode.encap) {
3408c2ecf20Sopenharmony_ci	case XFRM_MODE_BEET:
3418c2ecf20Sopenharmony_ci		return xfrm4_beet_encap_add(x, skb);
3428c2ecf20Sopenharmony_ci	case XFRM_MODE_TUNNEL:
3438c2ecf20Sopenharmony_ci		return xfrm4_tunnel_encap_add(x, skb);
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	WARN_ON_ONCE(1);
3478c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
3538c2ecf20Sopenharmony_ci	int err;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	err = xfrm_inner_extract_output(x, skb);
3568c2ecf20Sopenharmony_ci	if (err)
3578c2ecf20Sopenharmony_ci		return err;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	skb->ignore_df = 1;
3608c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_IPV6);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	switch (x->outer_mode.encap) {
3638c2ecf20Sopenharmony_ci	case XFRM_MODE_BEET:
3648c2ecf20Sopenharmony_ci		return xfrm6_beet_encap_add(x, skb);
3658c2ecf20Sopenharmony_ci	case XFRM_MODE_TUNNEL:
3668c2ecf20Sopenharmony_ci		return xfrm6_tunnel_encap_add(x, skb);
3678c2ecf20Sopenharmony_ci	default:
3688c2ecf20Sopenharmony_ci		WARN_ON_ONCE(1);
3698c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci#endif
3728c2ecf20Sopenharmony_ci	WARN_ON_ONCE(1);
3738c2ecf20Sopenharmony_ci	return -EAFNOSUPPORT;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic int xfrm_outer_mode_output(struct xfrm_state *x, struct sk_buff *skb)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	switch (x->outer_mode.encap) {
3798c2ecf20Sopenharmony_ci	case XFRM_MODE_BEET:
3808c2ecf20Sopenharmony_ci	case XFRM_MODE_TUNNEL:
3818c2ecf20Sopenharmony_ci		if (x->outer_mode.family == AF_INET)
3828c2ecf20Sopenharmony_ci			return xfrm4_prepare_output(x, skb);
3838c2ecf20Sopenharmony_ci		if (x->outer_mode.family == AF_INET6)
3848c2ecf20Sopenharmony_ci			return xfrm6_prepare_output(x, skb);
3858c2ecf20Sopenharmony_ci		break;
3868c2ecf20Sopenharmony_ci	case XFRM_MODE_TRANSPORT:
3878c2ecf20Sopenharmony_ci		if (x->outer_mode.family == AF_INET)
3888c2ecf20Sopenharmony_ci			return xfrm4_transport_output(x, skb);
3898c2ecf20Sopenharmony_ci		if (x->outer_mode.family == AF_INET6)
3908c2ecf20Sopenharmony_ci			return xfrm6_transport_output(x, skb);
3918c2ecf20Sopenharmony_ci		break;
3928c2ecf20Sopenharmony_ci	case XFRM_MODE_ROUTEOPTIMIZATION:
3938c2ecf20Sopenharmony_ci		if (x->outer_mode.family == AF_INET6)
3948c2ecf20Sopenharmony_ci			return xfrm6_ro_output(x, skb);
3958c2ecf20Sopenharmony_ci		WARN_ON_ONCE(1);
3968c2ecf20Sopenharmony_ci		break;
3978c2ecf20Sopenharmony_ci	default:
3988c2ecf20Sopenharmony_ci		WARN_ON_ONCE(1);
3998c2ecf20Sopenharmony_ci		break;
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_PKTGEN)
4068c2ecf20Sopenharmony_ciint pktgen_xfrm_outer_mode_output(struct xfrm_state *x, struct sk_buff *skb)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	return xfrm_outer_mode_output(x, skb);
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pktgen_xfrm_outer_mode_output);
4118c2ecf20Sopenharmony_ci#endif
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic int xfrm_output_one(struct sk_buff *skb, int err)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
4168c2ecf20Sopenharmony_ci	struct xfrm_state *x = dst->xfrm;
4178c2ecf20Sopenharmony_ci	struct net *net = xs_net(x);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if (err <= 0)
4208c2ecf20Sopenharmony_ci		goto resume;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	do {
4238c2ecf20Sopenharmony_ci		err = xfrm_skb_check_space(skb);
4248c2ecf20Sopenharmony_ci		if (err) {
4258c2ecf20Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
4268c2ecf20Sopenharmony_ci			goto error_nolock;
4278c2ecf20Sopenharmony_ci		}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		skb->mark = xfrm_smark_get(skb->mark, x);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		err = xfrm_outer_mode_output(x, skb);
4328c2ecf20Sopenharmony_ci		if (err) {
4338c2ecf20Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEMODEERROR);
4348c2ecf20Sopenharmony_ci			goto error_nolock;
4358c2ecf20Sopenharmony_ci		}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		spin_lock_bh(&x->lock);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci		if (unlikely(x->km.state != XFRM_STATE_VALID)) {
4408c2ecf20Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEINVALID);
4418c2ecf20Sopenharmony_ci			err = -EINVAL;
4428c2ecf20Sopenharmony_ci			goto error;
4438c2ecf20Sopenharmony_ci		}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		err = xfrm_state_check_expire(x);
4468c2ecf20Sopenharmony_ci		if (err) {
4478c2ecf20Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEEXPIRED);
4488c2ecf20Sopenharmony_ci			goto error;
4498c2ecf20Sopenharmony_ci		}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci		err = x->repl->overflow(x, skb);
4528c2ecf20Sopenharmony_ci		if (err) {
4538c2ecf20Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATESEQERROR);
4548c2ecf20Sopenharmony_ci			goto error;
4558c2ecf20Sopenharmony_ci		}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci		x->curlft.bytes += skb->len;
4588c2ecf20Sopenharmony_ci		x->curlft.packets++;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		spin_unlock_bh(&x->lock);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci		skb_dst_force(skb);
4638c2ecf20Sopenharmony_ci		if (!skb_dst(skb)) {
4648c2ecf20Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
4658c2ecf20Sopenharmony_ci			err = -EHOSTUNREACH;
4668c2ecf20Sopenharmony_ci			goto error_nolock;
4678c2ecf20Sopenharmony_ci		}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci		if (xfrm_offload(skb)) {
4708c2ecf20Sopenharmony_ci			x->type_offload->encap(x, skb);
4718c2ecf20Sopenharmony_ci		} else {
4728c2ecf20Sopenharmony_ci			/* Inner headers are invalid now. */
4738c2ecf20Sopenharmony_ci			skb->encapsulation = 0;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci			err = x->type->output(x, skb);
4768c2ecf20Sopenharmony_ci			if (err == -EINPROGRESS)
4778c2ecf20Sopenharmony_ci				goto out;
4788c2ecf20Sopenharmony_ci		}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ciresume:
4818c2ecf20Sopenharmony_ci		if (err) {
4828c2ecf20Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEPROTOERROR);
4838c2ecf20Sopenharmony_ci			goto error_nolock;
4848c2ecf20Sopenharmony_ci		}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci		dst = skb_dst_pop(skb);
4878c2ecf20Sopenharmony_ci		if (!dst) {
4888c2ecf20Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
4898c2ecf20Sopenharmony_ci			err = -EHOSTUNREACH;
4908c2ecf20Sopenharmony_ci			goto error_nolock;
4918c2ecf20Sopenharmony_ci		}
4928c2ecf20Sopenharmony_ci		skb_dst_set(skb, dst);
4938c2ecf20Sopenharmony_ci		x = dst->xfrm;
4948c2ecf20Sopenharmony_ci	} while (x && !(x->outer_mode.flags & XFRM_MODE_FLAG_TUNNEL));
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	return 0;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cierror:
4998c2ecf20Sopenharmony_ci	spin_unlock_bh(&x->lock);
5008c2ecf20Sopenharmony_cierror_nolock:
5018c2ecf20Sopenharmony_ci	kfree_skb(skb);
5028c2ecf20Sopenharmony_ciout:
5038c2ecf20Sopenharmony_ci	return err;
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ciint xfrm_output_resume(struct sock *sk, struct sk_buff *skb, int err)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	struct net *net = xs_net(skb_dst(skb)->xfrm);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	while (likely((err = xfrm_output_one(skb, err)) == 0)) {
5118c2ecf20Sopenharmony_ci		nf_reset_ct(skb);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci		err = skb_dst(skb)->ops->local_out(net, sk, skb);
5148c2ecf20Sopenharmony_ci		if (unlikely(err != 1))
5158c2ecf20Sopenharmony_ci			goto out;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		if (!skb_dst(skb)->xfrm)
5188c2ecf20Sopenharmony_ci			return dst_output(net, sk, skb);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci		err = nf_hook(skb_dst(skb)->ops->family,
5218c2ecf20Sopenharmony_ci			      NF_INET_POST_ROUTING, net, sk, skb,
5228c2ecf20Sopenharmony_ci			      NULL, skb_dst(skb)->dev, xfrm_output2);
5238c2ecf20Sopenharmony_ci		if (unlikely(err != 1))
5248c2ecf20Sopenharmony_ci			goto out;
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	if (err == -EINPROGRESS)
5288c2ecf20Sopenharmony_ci		err = 0;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ciout:
5318c2ecf20Sopenharmony_ci	return err;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_output_resume);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic int xfrm_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	return xfrm_output_resume(sk, skb, 1);
5388c2ecf20Sopenharmony_ci}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cistatic int xfrm_output_gso(struct net *net, struct sock *sk, struct sk_buff *skb)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	struct sk_buff *segs, *nskb;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(*IPCB(skb)) > SKB_GSO_CB_OFFSET);
5458c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(*IP6CB(skb)) > SKB_GSO_CB_OFFSET);
5468c2ecf20Sopenharmony_ci	segs = skb_gso_segment(skb, 0);
5478c2ecf20Sopenharmony_ci	kfree_skb(skb);
5488c2ecf20Sopenharmony_ci	if (IS_ERR(segs))
5498c2ecf20Sopenharmony_ci		return PTR_ERR(segs);
5508c2ecf20Sopenharmony_ci	if (segs == NULL)
5518c2ecf20Sopenharmony_ci		return -EINVAL;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	skb_list_walk_safe(segs, segs, nskb) {
5548c2ecf20Sopenharmony_ci		int err;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci		skb_mark_not_on_list(segs);
5578c2ecf20Sopenharmony_ci		err = xfrm_output2(net, sk, segs);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci		if (unlikely(err)) {
5608c2ecf20Sopenharmony_ci			kfree_skb_list(nskb);
5618c2ecf20Sopenharmony_ci			return err;
5628c2ecf20Sopenharmony_ci		}
5638c2ecf20Sopenharmony_ci	}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	return 0;
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ciint xfrm_output(struct sock *sk, struct sk_buff *skb)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	struct net *net = dev_net(skb_dst(skb)->dev);
5718c2ecf20Sopenharmony_ci	struct xfrm_state *x = skb_dst(skb)->xfrm;
5728c2ecf20Sopenharmony_ci	int err;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	switch (x->outer_mode.family) {
5758c2ecf20Sopenharmony_ci	case AF_INET:
5768c2ecf20Sopenharmony_ci		memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
5778c2ecf20Sopenharmony_ci		IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
5788c2ecf20Sopenharmony_ci		break;
5798c2ecf20Sopenharmony_ci	case AF_INET6:
5808c2ecf20Sopenharmony_ci		memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci		IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED;
5838c2ecf20Sopenharmony_ci		break;
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	secpath_reset(skb);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	if (xfrm_dev_offload_ok(skb, x)) {
5898c2ecf20Sopenharmony_ci		struct sec_path *sp;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci		sp = secpath_set(skb);
5928c2ecf20Sopenharmony_ci		if (!sp) {
5938c2ecf20Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
5948c2ecf20Sopenharmony_ci			kfree_skb(skb);
5958c2ecf20Sopenharmony_ci			return -ENOMEM;
5968c2ecf20Sopenharmony_ci		}
5978c2ecf20Sopenharmony_ci		skb->encapsulation = 1;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci		sp->olen++;
6008c2ecf20Sopenharmony_ci		sp->xvec[sp->len++] = x;
6018c2ecf20Sopenharmony_ci		xfrm_state_hold(x);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci		if (skb_is_gso(skb)) {
6048c2ecf20Sopenharmony_ci			if (skb->inner_protocol)
6058c2ecf20Sopenharmony_ci				return xfrm_output_gso(net, sk, skb);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci			skb_shinfo(skb)->gso_type |= SKB_GSO_ESP;
6088c2ecf20Sopenharmony_ci			goto out;
6098c2ecf20Sopenharmony_ci		}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci		if (x->xso.dev && x->xso.dev->features & NETIF_F_HW_ESP_TX_CSUM)
6128c2ecf20Sopenharmony_ci			goto out;
6138c2ecf20Sopenharmony_ci	} else {
6148c2ecf20Sopenharmony_ci		if (skb_is_gso(skb))
6158c2ecf20Sopenharmony_ci			return xfrm_output_gso(net, sk, skb);
6168c2ecf20Sopenharmony_ci	}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL) {
6198c2ecf20Sopenharmony_ci		err = skb_checksum_help(skb);
6208c2ecf20Sopenharmony_ci		if (err) {
6218c2ecf20Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
6228c2ecf20Sopenharmony_ci			kfree_skb(skb);
6238c2ecf20Sopenharmony_ci			return err;
6248c2ecf20Sopenharmony_ci		}
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ciout:
6288c2ecf20Sopenharmony_ci	return xfrm_output2(net, sk, skb);
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_output);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_cistatic int xfrm4_tunnel_check_size(struct sk_buff *skb)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	int mtu, ret = 0;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE)
6378c2ecf20Sopenharmony_ci		goto out;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->ignore_df)
6408c2ecf20Sopenharmony_ci		goto out;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	mtu = dst_mtu(skb_dst(skb));
6438c2ecf20Sopenharmony_ci	if ((!skb_is_gso(skb) && skb->len > mtu) ||
6448c2ecf20Sopenharmony_ci	    (skb_is_gso(skb) &&
6458c2ecf20Sopenharmony_ci	     !skb_gso_validate_network_len(skb, ip_skb_dst_mtu(skb->sk, skb)))) {
6468c2ecf20Sopenharmony_ci		skb->protocol = htons(ETH_P_IP);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci		if (skb->sk)
6498c2ecf20Sopenharmony_ci			xfrm_local_error(skb, mtu);
6508c2ecf20Sopenharmony_ci		else
6518c2ecf20Sopenharmony_ci			icmp_send(skb, ICMP_DEST_UNREACH,
6528c2ecf20Sopenharmony_ci				  ICMP_FRAG_NEEDED, htonl(mtu));
6538c2ecf20Sopenharmony_ci		ret = -EMSGSIZE;
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ciout:
6568c2ecf20Sopenharmony_ci	return ret;
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_cistatic int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	int err;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	if (x->outer_mode.encap == XFRM_MODE_BEET &&
6648c2ecf20Sopenharmony_ci	    ip_is_fragment(ip_hdr(skb))) {
6658c2ecf20Sopenharmony_ci		net_warn_ratelimited("BEET mode doesn't support inner IPv4 fragments\n");
6668c2ecf20Sopenharmony_ci		return -EAFNOSUPPORT;
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	err = xfrm4_tunnel_check_size(skb);
6708c2ecf20Sopenharmony_ci	if (err)
6718c2ecf20Sopenharmony_ci		return err;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	XFRM_MODE_SKB_CB(skb)->protocol = ip_hdr(skb)->protocol;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	xfrm4_extract_header(skb);
6768c2ecf20Sopenharmony_ci	return 0;
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
6808c2ecf20Sopenharmony_cistatic int xfrm6_tunnel_check_size(struct sk_buff *skb)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	int mtu, ret = 0;
6838c2ecf20Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	if (skb->ignore_df)
6868c2ecf20Sopenharmony_ci		goto out;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	mtu = dst_mtu(dst);
6898c2ecf20Sopenharmony_ci	if (mtu < IPV6_MIN_MTU)
6908c2ecf20Sopenharmony_ci		mtu = IPV6_MIN_MTU;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	if ((!skb_is_gso(skb) && skb->len > mtu) ||
6938c2ecf20Sopenharmony_ci	    (skb_is_gso(skb) &&
6948c2ecf20Sopenharmony_ci	     !skb_gso_validate_network_len(skb, ip6_skb_dst_mtu(skb)))) {
6958c2ecf20Sopenharmony_ci		skb->dev = dst->dev;
6968c2ecf20Sopenharmony_ci		skb->protocol = htons(ETH_P_IPV6);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci		if (xfrm6_local_dontfrag(skb->sk))
6998c2ecf20Sopenharmony_ci			ipv6_stub->xfrm6_local_rxpmtu(skb, mtu);
7008c2ecf20Sopenharmony_ci		else if (skb->sk)
7018c2ecf20Sopenharmony_ci			xfrm_local_error(skb, mtu);
7028c2ecf20Sopenharmony_ci		else
7038c2ecf20Sopenharmony_ci			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
7048c2ecf20Sopenharmony_ci		ret = -EMSGSIZE;
7058c2ecf20Sopenharmony_ci	}
7068c2ecf20Sopenharmony_ciout:
7078c2ecf20Sopenharmony_ci	return ret;
7088c2ecf20Sopenharmony_ci}
7098c2ecf20Sopenharmony_ci#endif
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_cistatic int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
7148c2ecf20Sopenharmony_ci	int err;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	err = xfrm6_tunnel_check_size(skb);
7178c2ecf20Sopenharmony_ci	if (err)
7188c2ecf20Sopenharmony_ci		return err;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	XFRM_MODE_SKB_CB(skb)->protocol = ipv6_hdr(skb)->nexthdr;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	xfrm6_extract_header(skb);
7238c2ecf20Sopenharmony_ci	return 0;
7248c2ecf20Sopenharmony_ci#else
7258c2ecf20Sopenharmony_ci	WARN_ON_ONCE(1);
7268c2ecf20Sopenharmony_ci	return -EAFNOSUPPORT;
7278c2ecf20Sopenharmony_ci#endif
7288c2ecf20Sopenharmony_ci}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_cistatic int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
7318c2ecf20Sopenharmony_ci{
7328c2ecf20Sopenharmony_ci	const struct xfrm_mode *inner_mode;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	if (x->sel.family == AF_UNSPEC)
7358c2ecf20Sopenharmony_ci		inner_mode = xfrm_ip2inner_mode(x,
7368c2ecf20Sopenharmony_ci				xfrm_af2proto(skb_dst(skb)->ops->family));
7378c2ecf20Sopenharmony_ci	else
7388c2ecf20Sopenharmony_ci		inner_mode = &x->inner_mode;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	if (inner_mode == NULL)
7418c2ecf20Sopenharmony_ci		return -EAFNOSUPPORT;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	switch (inner_mode->family) {
7448c2ecf20Sopenharmony_ci	case AF_INET:
7458c2ecf20Sopenharmony_ci		return xfrm4_extract_output(x, skb);
7468c2ecf20Sopenharmony_ci	case AF_INET6:
7478c2ecf20Sopenharmony_ci		return xfrm6_extract_output(x, skb);
7488c2ecf20Sopenharmony_ci	}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	return -EAFNOSUPPORT;
7518c2ecf20Sopenharmony_ci}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_civoid xfrm_local_error(struct sk_buff *skb, int mtu)
7548c2ecf20Sopenharmony_ci{
7558c2ecf20Sopenharmony_ci	unsigned int proto;
7568c2ecf20Sopenharmony_ci	struct xfrm_state_afinfo *afinfo;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IP))
7598c2ecf20Sopenharmony_ci		proto = AF_INET;
7608c2ecf20Sopenharmony_ci	else if (skb->protocol == htons(ETH_P_IPV6) &&
7618c2ecf20Sopenharmony_ci		 skb->sk->sk_family == AF_INET6)
7628c2ecf20Sopenharmony_ci		proto = AF_INET6;
7638c2ecf20Sopenharmony_ci	else
7648c2ecf20Sopenharmony_ci		return;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	afinfo = xfrm_state_get_afinfo(proto);
7678c2ecf20Sopenharmony_ci	if (afinfo) {
7688c2ecf20Sopenharmony_ci		afinfo->local_error(skb, mtu);
7698c2ecf20Sopenharmony_ci		rcu_read_unlock();
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci}
7728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_local_error);
773