162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * xfrm_output.c - Common IPsec encapsulation code.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/errno.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/netdevice.h>
1162306a36Sopenharmony_ci#include <linux/netfilter.h>
1262306a36Sopenharmony_ci#include <linux/skbuff.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/spinlock.h>
1562306a36Sopenharmony_ci#include <net/dst.h>
1662306a36Sopenharmony_ci#include <net/gso.h>
1762306a36Sopenharmony_ci#include <net/icmp.h>
1862306a36Sopenharmony_ci#include <net/inet_ecn.h>
1962306a36Sopenharmony_ci#include <net/xfrm.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
2262306a36Sopenharmony_ci#include <net/ip6_route.h>
2362306a36Sopenharmony_ci#include <net/ipv6_stubs.h>
2462306a36Sopenharmony_ci#endif
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "xfrm_inout.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic int xfrm_output2(struct net *net, struct sock *sk, struct sk_buff *skb);
2962306a36Sopenharmony_cistatic int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int xfrm_skb_check_space(struct sk_buff *skb)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
3462306a36Sopenharmony_ci	int nhead = dst->header_len + LL_RESERVED_SPACE(dst->dev)
3562306a36Sopenharmony_ci		- skb_headroom(skb);
3662306a36Sopenharmony_ci	int ntail = dst->dev->needed_tailroom - skb_tailroom(skb);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (nhead <= 0) {
3962306a36Sopenharmony_ci		if (ntail <= 0)
4062306a36Sopenharmony_ci			return 0;
4162306a36Sopenharmony_ci		nhead = 0;
4262306a36Sopenharmony_ci	} else if (ntail < 0)
4362306a36Sopenharmony_ci		ntail = 0;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	return pskb_expand_head(skb, nhead, ntail, GFP_ATOMIC);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* Children define the path of the packet through the
4962306a36Sopenharmony_ci * Linux networking.  Thus, destinations are stackable.
5062306a36Sopenharmony_ci */
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic struct dst_entry *skb_dst_pop(struct sk_buff *skb)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct dst_entry *child = dst_clone(xfrm_dst_child(skb_dst(skb)));
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	skb_dst_drop(skb);
5762306a36Sopenharmony_ci	return child;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* Add encapsulation header.
6162306a36Sopenharmony_ci *
6262306a36Sopenharmony_ci * The IP header will be moved forward to make space for the encapsulation
6362306a36Sopenharmony_ci * header.
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_cistatic int xfrm4_transport_output(struct xfrm_state *x, struct sk_buff *skb)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct iphdr *iph = ip_hdr(skb);
6862306a36Sopenharmony_ci	int ihl = iph->ihl * 4;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	skb_set_inner_transport_header(skb, skb_transport_offset(skb));
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	skb_set_network_header(skb, -x->props.header_len);
7362306a36Sopenharmony_ci	skb->mac_header = skb->network_header +
7462306a36Sopenharmony_ci			  offsetof(struct iphdr, protocol);
7562306a36Sopenharmony_ci	skb->transport_header = skb->network_header + ihl;
7662306a36Sopenharmony_ci	__skb_pull(skb, ihl);
7762306a36Sopenharmony_ci	memmove(skb_network_header(skb), iph, ihl);
7862306a36Sopenharmony_ci	return 0;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6)
8262306a36Sopenharmony_cistatic int mip6_rthdr_offset(struct sk_buff *skb, u8 **nexthdr, int type)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	const unsigned char *nh = skb_network_header(skb);
8562306a36Sopenharmony_ci	unsigned int offset = sizeof(struct ipv6hdr);
8662306a36Sopenharmony_ci	unsigned int packet_len;
8762306a36Sopenharmony_ci	int found_rhdr = 0;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	packet_len = skb_tail_pointer(skb) - nh;
9062306a36Sopenharmony_ci	*nexthdr = &ipv6_hdr(skb)->nexthdr;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	while (offset <= packet_len) {
9362306a36Sopenharmony_ci		struct ipv6_opt_hdr *exthdr;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		switch (**nexthdr) {
9662306a36Sopenharmony_ci		case NEXTHDR_HOP:
9762306a36Sopenharmony_ci			break;
9862306a36Sopenharmony_ci		case NEXTHDR_ROUTING:
9962306a36Sopenharmony_ci			if (type == IPPROTO_ROUTING && offset + 3 <= packet_len) {
10062306a36Sopenharmony_ci				struct ipv6_rt_hdr *rt;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci				rt = (struct ipv6_rt_hdr *)(nh + offset);
10362306a36Sopenharmony_ci				if (rt->type != 0)
10462306a36Sopenharmony_ci					return offset;
10562306a36Sopenharmony_ci			}
10662306a36Sopenharmony_ci			found_rhdr = 1;
10762306a36Sopenharmony_ci			break;
10862306a36Sopenharmony_ci		case NEXTHDR_DEST:
10962306a36Sopenharmony_ci			/* HAO MUST NOT appear more than once.
11062306a36Sopenharmony_ci			 * XXX: It is better to try to find by the end of
11162306a36Sopenharmony_ci			 * XXX: packet if HAO exists.
11262306a36Sopenharmony_ci			 */
11362306a36Sopenharmony_ci			if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0) {
11462306a36Sopenharmony_ci				net_dbg_ratelimited("mip6: hao exists already, override\n");
11562306a36Sopenharmony_ci				return offset;
11662306a36Sopenharmony_ci			}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci			if (found_rhdr)
11962306a36Sopenharmony_ci				return offset;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci			break;
12262306a36Sopenharmony_ci		default:
12362306a36Sopenharmony_ci			return offset;
12462306a36Sopenharmony_ci		}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci		if (offset + sizeof(struct ipv6_opt_hdr) > packet_len)
12762306a36Sopenharmony_ci			return -EINVAL;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) +
13062306a36Sopenharmony_ci						 offset);
13162306a36Sopenharmony_ci		offset += ipv6_optlen(exthdr);
13262306a36Sopenharmony_ci		if (offset > IPV6_MAXPLEN)
13362306a36Sopenharmony_ci			return -EINVAL;
13462306a36Sopenharmony_ci		*nexthdr = &exthdr->nexthdr;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return -EINVAL;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci#endif
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
14262306a36Sopenharmony_cistatic int xfrm6_hdr_offset(struct xfrm_state *x, struct sk_buff *skb, u8 **prevhdr)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	switch (x->type->proto) {
14562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6)
14662306a36Sopenharmony_ci	case IPPROTO_DSTOPTS:
14762306a36Sopenharmony_ci	case IPPROTO_ROUTING:
14862306a36Sopenharmony_ci		return mip6_rthdr_offset(skb, prevhdr, x->type->proto);
14962306a36Sopenharmony_ci#endif
15062306a36Sopenharmony_ci	default:
15162306a36Sopenharmony_ci		break;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return ip6_find_1stfragopt(skb, prevhdr);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci#endif
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/* Add encapsulation header.
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci * The IP header and mutable extension headers will be moved forward to make
16162306a36Sopenharmony_ci * space for the encapsulation header.
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_cistatic int xfrm6_transport_output(struct xfrm_state *x, struct sk_buff *skb)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
16662306a36Sopenharmony_ci	struct ipv6hdr *iph;
16762306a36Sopenharmony_ci	u8 *prevhdr;
16862306a36Sopenharmony_ci	int hdr_len;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	iph = ipv6_hdr(skb);
17162306a36Sopenharmony_ci	skb_set_inner_transport_header(skb, skb_transport_offset(skb));
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	hdr_len = xfrm6_hdr_offset(x, skb, &prevhdr);
17462306a36Sopenharmony_ci	if (hdr_len < 0)
17562306a36Sopenharmony_ci		return hdr_len;
17662306a36Sopenharmony_ci	skb_set_mac_header(skb,
17762306a36Sopenharmony_ci			   (prevhdr - x->props.header_len) - skb->data);
17862306a36Sopenharmony_ci	skb_set_network_header(skb, -x->props.header_len);
17962306a36Sopenharmony_ci	skb->transport_header = skb->network_header + hdr_len;
18062306a36Sopenharmony_ci	__skb_pull(skb, hdr_len);
18162306a36Sopenharmony_ci	memmove(ipv6_hdr(skb), iph, hdr_len);
18262306a36Sopenharmony_ci	return 0;
18362306a36Sopenharmony_ci#else
18462306a36Sopenharmony_ci	WARN_ON_ONCE(1);
18562306a36Sopenharmony_ci	return -EAFNOSUPPORT;
18662306a36Sopenharmony_ci#endif
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci/* Add route optimization header space.
19062306a36Sopenharmony_ci *
19162306a36Sopenharmony_ci * The IP header and mutable extension headers will be moved forward to make
19262306a36Sopenharmony_ci * space for the route optimization header.
19362306a36Sopenharmony_ci */
19462306a36Sopenharmony_cistatic int xfrm6_ro_output(struct xfrm_state *x, struct sk_buff *skb)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
19762306a36Sopenharmony_ci	struct ipv6hdr *iph;
19862306a36Sopenharmony_ci	u8 *prevhdr;
19962306a36Sopenharmony_ci	int hdr_len;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	iph = ipv6_hdr(skb);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	hdr_len = xfrm6_hdr_offset(x, skb, &prevhdr);
20462306a36Sopenharmony_ci	if (hdr_len < 0)
20562306a36Sopenharmony_ci		return hdr_len;
20662306a36Sopenharmony_ci	skb_set_mac_header(skb,
20762306a36Sopenharmony_ci			   (prevhdr - x->props.header_len) - skb->data);
20862306a36Sopenharmony_ci	skb_set_network_header(skb, -x->props.header_len);
20962306a36Sopenharmony_ci	skb->transport_header = skb->network_header + hdr_len;
21062306a36Sopenharmony_ci	__skb_pull(skb, hdr_len);
21162306a36Sopenharmony_ci	memmove(ipv6_hdr(skb), iph, hdr_len);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	return 0;
21462306a36Sopenharmony_ci#else
21562306a36Sopenharmony_ci	WARN_ON_ONCE(1);
21662306a36Sopenharmony_ci	return -EAFNOSUPPORT;
21762306a36Sopenharmony_ci#endif
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci/* Add encapsulation header.
22162306a36Sopenharmony_ci *
22262306a36Sopenharmony_ci * The top IP header will be constructed per draft-nikander-esp-beet-mode-06.txt.
22362306a36Sopenharmony_ci */
22462306a36Sopenharmony_cistatic int xfrm4_beet_encap_add(struct xfrm_state *x, struct sk_buff *skb)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct ip_beet_phdr *ph;
22762306a36Sopenharmony_ci	struct iphdr *top_iph;
22862306a36Sopenharmony_ci	int hdrlen, optlen;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	hdrlen = 0;
23162306a36Sopenharmony_ci	optlen = XFRM_MODE_SKB_CB(skb)->optlen;
23262306a36Sopenharmony_ci	if (unlikely(optlen))
23362306a36Sopenharmony_ci		hdrlen += IPV4_BEET_PHMAXLEN - (optlen & 4);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	skb_set_network_header(skb, -x->props.header_len - hdrlen +
23662306a36Sopenharmony_ci			       (XFRM_MODE_SKB_CB(skb)->ihl - sizeof(*top_iph)));
23762306a36Sopenharmony_ci	if (x->sel.family != AF_INET6)
23862306a36Sopenharmony_ci		skb->network_header += IPV4_BEET_PHMAXLEN;
23962306a36Sopenharmony_ci	skb->mac_header = skb->network_header +
24062306a36Sopenharmony_ci			  offsetof(struct iphdr, protocol);
24162306a36Sopenharmony_ci	skb->transport_header = skb->network_header + sizeof(*top_iph);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	xfrm4_beet_make_header(skb);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	ph = __skb_pull(skb, XFRM_MODE_SKB_CB(skb)->ihl - hdrlen);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	top_iph = ip_hdr(skb);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (unlikely(optlen)) {
25062306a36Sopenharmony_ci		if (WARN_ON(optlen < 0))
25162306a36Sopenharmony_ci			return -EINVAL;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		ph->padlen = 4 - (optlen & 4);
25462306a36Sopenharmony_ci		ph->hdrlen = optlen / 8;
25562306a36Sopenharmony_ci		ph->nexthdr = top_iph->protocol;
25662306a36Sopenharmony_ci		if (ph->padlen)
25762306a36Sopenharmony_ci			memset(ph + 1, IPOPT_NOP, ph->padlen);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		top_iph->protocol = IPPROTO_BEETPH;
26062306a36Sopenharmony_ci		top_iph->ihl = sizeof(struct iphdr) / 4;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	top_iph->saddr = x->props.saddr.a4;
26462306a36Sopenharmony_ci	top_iph->daddr = x->id.daddr.a4;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	return 0;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci/* Add encapsulation header.
27062306a36Sopenharmony_ci *
27162306a36Sopenharmony_ci * The top IP header will be constructed per RFC 2401.
27262306a36Sopenharmony_ci */
27362306a36Sopenharmony_cistatic int xfrm4_tunnel_encap_add(struct xfrm_state *x, struct sk_buff *skb)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	bool small_ipv6 = (skb->protocol == htons(ETH_P_IPV6)) && (skb->len <= IPV6_MIN_MTU);
27662306a36Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
27762306a36Sopenharmony_ci	struct iphdr *top_iph;
27862306a36Sopenharmony_ci	int flags;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	skb_set_inner_network_header(skb, skb_network_offset(skb));
28162306a36Sopenharmony_ci	skb_set_inner_transport_header(skb, skb_transport_offset(skb));
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	skb_set_network_header(skb, -x->props.header_len);
28462306a36Sopenharmony_ci	skb->mac_header = skb->network_header +
28562306a36Sopenharmony_ci			  offsetof(struct iphdr, protocol);
28662306a36Sopenharmony_ci	skb->transport_header = skb->network_header + sizeof(*top_iph);
28762306a36Sopenharmony_ci	top_iph = ip_hdr(skb);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	top_iph->ihl = 5;
29062306a36Sopenharmony_ci	top_iph->version = 4;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	top_iph->protocol = xfrm_af2proto(skb_dst(skb)->ops->family);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	/* DS disclosing depends on XFRM_SA_XFLAG_DONT_ENCAP_DSCP */
29562306a36Sopenharmony_ci	if (x->props.extra_flags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP)
29662306a36Sopenharmony_ci		top_iph->tos = 0;
29762306a36Sopenharmony_ci	else
29862306a36Sopenharmony_ci		top_iph->tos = XFRM_MODE_SKB_CB(skb)->tos;
29962306a36Sopenharmony_ci	top_iph->tos = INET_ECN_encapsulate(top_iph->tos,
30062306a36Sopenharmony_ci					    XFRM_MODE_SKB_CB(skb)->tos);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	flags = x->props.flags;
30362306a36Sopenharmony_ci	if (flags & XFRM_STATE_NOECN)
30462306a36Sopenharmony_ci		IP_ECN_clear(top_iph);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) || small_ipv6 ?
30762306a36Sopenharmony_ci		0 : (XFRM_MODE_SKB_CB(skb)->frag_off & htons(IP_DF));
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	top_iph->ttl = ip4_dst_hoplimit(xfrm_dst_child(dst));
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	top_iph->saddr = x->props.saddr.a4;
31262306a36Sopenharmony_ci	top_iph->daddr = x->id.daddr.a4;
31362306a36Sopenharmony_ci	ip_select_ident(dev_net(dst->dev), skb, NULL);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return 0;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
31962306a36Sopenharmony_cistatic int xfrm6_tunnel_encap_add(struct xfrm_state *x, struct sk_buff *skb)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
32262306a36Sopenharmony_ci	struct ipv6hdr *top_iph;
32362306a36Sopenharmony_ci	int dsfield;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	skb_set_inner_network_header(skb, skb_network_offset(skb));
32662306a36Sopenharmony_ci	skb_set_inner_transport_header(skb, skb_transport_offset(skb));
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	skb_set_network_header(skb, -x->props.header_len);
32962306a36Sopenharmony_ci	skb->mac_header = skb->network_header +
33062306a36Sopenharmony_ci			  offsetof(struct ipv6hdr, nexthdr);
33162306a36Sopenharmony_ci	skb->transport_header = skb->network_header + sizeof(*top_iph);
33262306a36Sopenharmony_ci	top_iph = ipv6_hdr(skb);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	top_iph->version = 6;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	memcpy(top_iph->flow_lbl, XFRM_MODE_SKB_CB(skb)->flow_lbl,
33762306a36Sopenharmony_ci	       sizeof(top_iph->flow_lbl));
33862306a36Sopenharmony_ci	top_iph->nexthdr = xfrm_af2proto(skb_dst(skb)->ops->family);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (x->props.extra_flags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP)
34162306a36Sopenharmony_ci		dsfield = 0;
34262306a36Sopenharmony_ci	else
34362306a36Sopenharmony_ci		dsfield = XFRM_MODE_SKB_CB(skb)->tos;
34462306a36Sopenharmony_ci	dsfield = INET_ECN_encapsulate(dsfield, XFRM_MODE_SKB_CB(skb)->tos);
34562306a36Sopenharmony_ci	if (x->props.flags & XFRM_STATE_NOECN)
34662306a36Sopenharmony_ci		dsfield &= ~INET_ECN_MASK;
34762306a36Sopenharmony_ci	ipv6_change_dsfield(top_iph, 0, dsfield);
34862306a36Sopenharmony_ci	top_iph->hop_limit = ip6_dst_hoplimit(xfrm_dst_child(dst));
34962306a36Sopenharmony_ci	top_iph->saddr = *(struct in6_addr *)&x->props.saddr;
35062306a36Sopenharmony_ci	top_iph->daddr = *(struct in6_addr *)&x->id.daddr;
35162306a36Sopenharmony_ci	return 0;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic int xfrm6_beet_encap_add(struct xfrm_state *x, struct sk_buff *skb)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct ipv6hdr *top_iph;
35762306a36Sopenharmony_ci	struct ip_beet_phdr *ph;
35862306a36Sopenharmony_ci	int optlen, hdr_len;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	hdr_len = 0;
36162306a36Sopenharmony_ci	optlen = XFRM_MODE_SKB_CB(skb)->optlen;
36262306a36Sopenharmony_ci	if (unlikely(optlen))
36362306a36Sopenharmony_ci		hdr_len += IPV4_BEET_PHMAXLEN - (optlen & 4);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	skb_set_network_header(skb, -x->props.header_len - hdr_len);
36662306a36Sopenharmony_ci	if (x->sel.family != AF_INET6)
36762306a36Sopenharmony_ci		skb->network_header += IPV4_BEET_PHMAXLEN;
36862306a36Sopenharmony_ci	skb->mac_header = skb->network_header +
36962306a36Sopenharmony_ci			  offsetof(struct ipv6hdr, nexthdr);
37062306a36Sopenharmony_ci	skb->transport_header = skb->network_header + sizeof(*top_iph);
37162306a36Sopenharmony_ci	ph = __skb_pull(skb, XFRM_MODE_SKB_CB(skb)->ihl - hdr_len);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	xfrm6_beet_make_header(skb);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	top_iph = ipv6_hdr(skb);
37662306a36Sopenharmony_ci	if (unlikely(optlen)) {
37762306a36Sopenharmony_ci		if (WARN_ON(optlen < 0))
37862306a36Sopenharmony_ci			return -EINVAL;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci		ph->padlen = 4 - (optlen & 4);
38162306a36Sopenharmony_ci		ph->hdrlen = optlen / 8;
38262306a36Sopenharmony_ci		ph->nexthdr = top_iph->nexthdr;
38362306a36Sopenharmony_ci		if (ph->padlen)
38462306a36Sopenharmony_ci			memset(ph + 1, IPOPT_NOP, ph->padlen);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		top_iph->nexthdr = IPPROTO_BEETPH;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	top_iph->saddr = *(struct in6_addr *)&x->props.saddr;
39062306a36Sopenharmony_ci	top_iph->daddr = *(struct in6_addr *)&x->id.daddr;
39162306a36Sopenharmony_ci	return 0;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci#endif
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci/* Add encapsulation header.
39662306a36Sopenharmony_ci *
39762306a36Sopenharmony_ci * On exit, the transport header will be set to the start of the
39862306a36Sopenharmony_ci * encapsulation header to be filled in by x->type->output and the mac
39962306a36Sopenharmony_ci * header will be set to the nextheader (protocol for IPv4) field of the
40062306a36Sopenharmony_ci * extension header directly preceding the encapsulation header, or in
40162306a36Sopenharmony_ci * its absence, that of the top IP header.
40262306a36Sopenharmony_ci * The value of the network header will always point to the top IP header
40362306a36Sopenharmony_ci * while skb->data will point to the payload.
40462306a36Sopenharmony_ci */
40562306a36Sopenharmony_cistatic int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	int err;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	err = xfrm_inner_extract_output(x, skb);
41062306a36Sopenharmony_ci	if (err)
41162306a36Sopenharmony_ci		return err;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE;
41462306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_IP);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	switch (x->props.mode) {
41762306a36Sopenharmony_ci	case XFRM_MODE_BEET:
41862306a36Sopenharmony_ci		return xfrm4_beet_encap_add(x, skb);
41962306a36Sopenharmony_ci	case XFRM_MODE_TUNNEL:
42062306a36Sopenharmony_ci		return xfrm4_tunnel_encap_add(x, skb);
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	WARN_ON_ONCE(1);
42462306a36Sopenharmony_ci	return -EOPNOTSUPP;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
43062306a36Sopenharmony_ci	int err;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	err = xfrm_inner_extract_output(x, skb);
43362306a36Sopenharmony_ci	if (err)
43462306a36Sopenharmony_ci		return err;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	skb->ignore_df = 1;
43762306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_IPV6);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	switch (x->props.mode) {
44062306a36Sopenharmony_ci	case XFRM_MODE_BEET:
44162306a36Sopenharmony_ci		return xfrm6_beet_encap_add(x, skb);
44262306a36Sopenharmony_ci	case XFRM_MODE_TUNNEL:
44362306a36Sopenharmony_ci		return xfrm6_tunnel_encap_add(x, skb);
44462306a36Sopenharmony_ci	default:
44562306a36Sopenharmony_ci		WARN_ON_ONCE(1);
44662306a36Sopenharmony_ci		return -EOPNOTSUPP;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci#endif
44962306a36Sopenharmony_ci	WARN_ON_ONCE(1);
45062306a36Sopenharmony_ci	return -EAFNOSUPPORT;
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic int xfrm_outer_mode_output(struct xfrm_state *x, struct sk_buff *skb)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	switch (x->props.mode) {
45662306a36Sopenharmony_ci	case XFRM_MODE_BEET:
45762306a36Sopenharmony_ci	case XFRM_MODE_TUNNEL:
45862306a36Sopenharmony_ci		if (x->props.family == AF_INET)
45962306a36Sopenharmony_ci			return xfrm4_prepare_output(x, skb);
46062306a36Sopenharmony_ci		if (x->props.family == AF_INET6)
46162306a36Sopenharmony_ci			return xfrm6_prepare_output(x, skb);
46262306a36Sopenharmony_ci		break;
46362306a36Sopenharmony_ci	case XFRM_MODE_TRANSPORT:
46462306a36Sopenharmony_ci		if (x->props.family == AF_INET)
46562306a36Sopenharmony_ci			return xfrm4_transport_output(x, skb);
46662306a36Sopenharmony_ci		if (x->props.family == AF_INET6)
46762306a36Sopenharmony_ci			return xfrm6_transport_output(x, skb);
46862306a36Sopenharmony_ci		break;
46962306a36Sopenharmony_ci	case XFRM_MODE_ROUTEOPTIMIZATION:
47062306a36Sopenharmony_ci		if (x->props.family == AF_INET6)
47162306a36Sopenharmony_ci			return xfrm6_ro_output(x, skb);
47262306a36Sopenharmony_ci		WARN_ON_ONCE(1);
47362306a36Sopenharmony_ci		break;
47462306a36Sopenharmony_ci	default:
47562306a36Sopenharmony_ci		WARN_ON_ONCE(1);
47662306a36Sopenharmony_ci		break;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	return -EOPNOTSUPP;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_PKTGEN)
48362306a36Sopenharmony_ciint pktgen_xfrm_outer_mode_output(struct xfrm_state *x, struct sk_buff *skb)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	return xfrm_outer_mode_output(x, skb);
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pktgen_xfrm_outer_mode_output);
48862306a36Sopenharmony_ci#endif
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic int xfrm_output_one(struct sk_buff *skb, int err)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
49362306a36Sopenharmony_ci	struct xfrm_state *x = dst->xfrm;
49462306a36Sopenharmony_ci	struct net *net = xs_net(x);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (err <= 0 || x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
49762306a36Sopenharmony_ci		goto resume;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	do {
50062306a36Sopenharmony_ci		err = xfrm_skb_check_space(skb);
50162306a36Sopenharmony_ci		if (err) {
50262306a36Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
50362306a36Sopenharmony_ci			goto error_nolock;
50462306a36Sopenharmony_ci		}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		skb->mark = xfrm_smark_get(skb->mark, x);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		err = xfrm_outer_mode_output(x, skb);
50962306a36Sopenharmony_ci		if (err) {
51062306a36Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEMODEERROR);
51162306a36Sopenharmony_ci			goto error_nolock;
51262306a36Sopenharmony_ci		}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		spin_lock_bh(&x->lock);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci		if (unlikely(x->km.state != XFRM_STATE_VALID)) {
51762306a36Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEINVALID);
51862306a36Sopenharmony_ci			err = -EINVAL;
51962306a36Sopenharmony_ci			goto error;
52062306a36Sopenharmony_ci		}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci		err = xfrm_state_check_expire(x);
52362306a36Sopenharmony_ci		if (err) {
52462306a36Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEEXPIRED);
52562306a36Sopenharmony_ci			goto error;
52662306a36Sopenharmony_ci		}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci		err = xfrm_replay_overflow(x, skb);
52962306a36Sopenharmony_ci		if (err) {
53062306a36Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATESEQERROR);
53162306a36Sopenharmony_ci			goto error;
53262306a36Sopenharmony_ci		}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci		x->curlft.bytes += skb->len;
53562306a36Sopenharmony_ci		x->curlft.packets++;
53662306a36Sopenharmony_ci		x->lastused = ktime_get_real_seconds();
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci		spin_unlock_bh(&x->lock);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci		skb_dst_force(skb);
54162306a36Sopenharmony_ci		if (!skb_dst(skb)) {
54262306a36Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
54362306a36Sopenharmony_ci			err = -EHOSTUNREACH;
54462306a36Sopenharmony_ci			goto error_nolock;
54562306a36Sopenharmony_ci		}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		if (xfrm_offload(skb)) {
54862306a36Sopenharmony_ci			x->type_offload->encap(x, skb);
54962306a36Sopenharmony_ci		} else {
55062306a36Sopenharmony_ci			/* Inner headers are invalid now. */
55162306a36Sopenharmony_ci			skb->encapsulation = 0;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci			err = x->type->output(x, skb);
55462306a36Sopenharmony_ci			if (err == -EINPROGRESS)
55562306a36Sopenharmony_ci				goto out;
55662306a36Sopenharmony_ci		}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ciresume:
55962306a36Sopenharmony_ci		if (err) {
56062306a36Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEPROTOERROR);
56162306a36Sopenharmony_ci			goto error_nolock;
56262306a36Sopenharmony_ci		}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		dst = skb_dst_pop(skb);
56562306a36Sopenharmony_ci		if (!dst) {
56662306a36Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
56762306a36Sopenharmony_ci			err = -EHOSTUNREACH;
56862306a36Sopenharmony_ci			goto error_nolock;
56962306a36Sopenharmony_ci		}
57062306a36Sopenharmony_ci		skb_dst_set(skb, dst);
57162306a36Sopenharmony_ci		x = dst->xfrm;
57262306a36Sopenharmony_ci	} while (x && !(x->outer_mode.flags & XFRM_MODE_FLAG_TUNNEL));
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	return 0;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cierror:
57762306a36Sopenharmony_ci	spin_unlock_bh(&x->lock);
57862306a36Sopenharmony_cierror_nolock:
57962306a36Sopenharmony_ci	kfree_skb(skb);
58062306a36Sopenharmony_ciout:
58162306a36Sopenharmony_ci	return err;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ciint xfrm_output_resume(struct sock *sk, struct sk_buff *skb, int err)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	struct net *net = xs_net(skb_dst(skb)->xfrm);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	while (likely((err = xfrm_output_one(skb, err)) == 0)) {
58962306a36Sopenharmony_ci		nf_reset_ct(skb);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		err = skb_dst(skb)->ops->local_out(net, sk, skb);
59262306a36Sopenharmony_ci		if (unlikely(err != 1))
59362306a36Sopenharmony_ci			goto out;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci		if (!skb_dst(skb)->xfrm)
59662306a36Sopenharmony_ci			return dst_output(net, sk, skb);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci		err = nf_hook(skb_dst(skb)->ops->family,
59962306a36Sopenharmony_ci			      NF_INET_POST_ROUTING, net, sk, skb,
60062306a36Sopenharmony_ci			      NULL, skb_dst(skb)->dev, xfrm_output2);
60162306a36Sopenharmony_ci		if (unlikely(err != 1))
60262306a36Sopenharmony_ci			goto out;
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	if (err == -EINPROGRESS)
60662306a36Sopenharmony_ci		err = 0;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ciout:
60962306a36Sopenharmony_ci	return err;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_output_resume);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic int xfrm_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	return xfrm_output_resume(sk, skb, 1);
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic int xfrm_output_gso(struct net *net, struct sock *sk, struct sk_buff *skb)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	struct sk_buff *segs, *nskb;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(*IPCB(skb)) > SKB_GSO_CB_OFFSET);
62362306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(*IP6CB(skb)) > SKB_GSO_CB_OFFSET);
62462306a36Sopenharmony_ci	segs = skb_gso_segment(skb, 0);
62562306a36Sopenharmony_ci	kfree_skb(skb);
62662306a36Sopenharmony_ci	if (IS_ERR(segs))
62762306a36Sopenharmony_ci		return PTR_ERR(segs);
62862306a36Sopenharmony_ci	if (segs == NULL)
62962306a36Sopenharmony_ci		return -EINVAL;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	skb_list_walk_safe(segs, segs, nskb) {
63262306a36Sopenharmony_ci		int err;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci		skb_mark_not_on_list(segs);
63562306a36Sopenharmony_ci		err = xfrm_output2(net, sk, segs);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci		if (unlikely(err)) {
63862306a36Sopenharmony_ci			kfree_skb_list(nskb);
63962306a36Sopenharmony_ci			return err;
64062306a36Sopenharmony_ci		}
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	return 0;
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci/* For partial checksum offload, the outer header checksum is calculated
64762306a36Sopenharmony_ci * by software and the inner header checksum is calculated by hardware.
64862306a36Sopenharmony_ci * This requires hardware to know the inner packet type to calculate
64962306a36Sopenharmony_ci * the inner header checksum. Save inner ip protocol here to avoid
65062306a36Sopenharmony_ci * traversing the packet in the vendor's xmit code.
65162306a36Sopenharmony_ci * For IPsec tunnel mode save the ip protocol from the IP header of the
65262306a36Sopenharmony_ci * plain text packet. Otherwise If the encap type is IPIP, just save
65362306a36Sopenharmony_ci * skb->inner_ipproto in any other case get the ip protocol from the IP
65462306a36Sopenharmony_ci * header.
65562306a36Sopenharmony_ci */
65662306a36Sopenharmony_cistatic void xfrm_get_inner_ipproto(struct sk_buff *skb, struct xfrm_state *x)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	struct xfrm_offload *xo = xfrm_offload(skb);
65962306a36Sopenharmony_ci	const struct ethhdr *eth;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	if (!xo)
66262306a36Sopenharmony_ci		return;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	if (x->outer_mode.encap == XFRM_MODE_TUNNEL) {
66562306a36Sopenharmony_ci		switch (x->outer_mode.family) {
66662306a36Sopenharmony_ci		case AF_INET:
66762306a36Sopenharmony_ci			xo->inner_ipproto = ip_hdr(skb)->protocol;
66862306a36Sopenharmony_ci			break;
66962306a36Sopenharmony_ci		case AF_INET6:
67062306a36Sopenharmony_ci			xo->inner_ipproto = ipv6_hdr(skb)->nexthdr;
67162306a36Sopenharmony_ci			break;
67262306a36Sopenharmony_ci		default:
67362306a36Sopenharmony_ci			break;
67462306a36Sopenharmony_ci		}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci		return;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	/* non-Tunnel Mode */
68062306a36Sopenharmony_ci	if (!skb->encapsulation)
68162306a36Sopenharmony_ci		return;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	if (skb->inner_protocol_type == ENCAP_TYPE_IPPROTO) {
68462306a36Sopenharmony_ci		xo->inner_ipproto = skb->inner_ipproto;
68562306a36Sopenharmony_ci		return;
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	if (skb->inner_protocol_type != ENCAP_TYPE_ETHER)
68962306a36Sopenharmony_ci		return;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	eth = (struct ethhdr *)skb_inner_mac_header(skb);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	switch (ntohs(eth->h_proto)) {
69462306a36Sopenharmony_ci	case ETH_P_IPV6:
69562306a36Sopenharmony_ci		xo->inner_ipproto = inner_ipv6_hdr(skb)->nexthdr;
69662306a36Sopenharmony_ci		break;
69762306a36Sopenharmony_ci	case ETH_P_IP:
69862306a36Sopenharmony_ci		xo->inner_ipproto = inner_ip_hdr(skb)->protocol;
69962306a36Sopenharmony_ci		break;
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ciint xfrm_output(struct sock *sk, struct sk_buff *skb)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	struct net *net = dev_net(skb_dst(skb)->dev);
70662306a36Sopenharmony_ci	struct xfrm_state *x = skb_dst(skb)->xfrm;
70762306a36Sopenharmony_ci	int family;
70862306a36Sopenharmony_ci	int err;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	family = (x->xso.type != XFRM_DEV_OFFLOAD_PACKET) ? x->outer_mode.family
71162306a36Sopenharmony_ci		: skb_dst(skb)->ops->family;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	switch (family) {
71462306a36Sopenharmony_ci	case AF_INET:
71562306a36Sopenharmony_ci		memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
71662306a36Sopenharmony_ci		IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
71762306a36Sopenharmony_ci		break;
71862306a36Sopenharmony_ci	case AF_INET6:
71962306a36Sopenharmony_ci		memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci		IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED;
72262306a36Sopenharmony_ci		break;
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) {
72662306a36Sopenharmony_ci		if (!xfrm_dev_offload_ok(skb, x)) {
72762306a36Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
72862306a36Sopenharmony_ci			kfree_skb(skb);
72962306a36Sopenharmony_ci			return -EHOSTUNREACH;
73062306a36Sopenharmony_ci		}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci		return xfrm_output_resume(sk, skb, 0);
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	secpath_reset(skb);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	if (xfrm_dev_offload_ok(skb, x)) {
73862306a36Sopenharmony_ci		struct sec_path *sp;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		sp = secpath_set(skb);
74162306a36Sopenharmony_ci		if (!sp) {
74262306a36Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
74362306a36Sopenharmony_ci			kfree_skb(skb);
74462306a36Sopenharmony_ci			return -ENOMEM;
74562306a36Sopenharmony_ci		}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci		sp->olen++;
74862306a36Sopenharmony_ci		sp->xvec[sp->len++] = x;
74962306a36Sopenharmony_ci		xfrm_state_hold(x);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci		xfrm_get_inner_ipproto(skb, x);
75262306a36Sopenharmony_ci		skb->encapsulation = 1;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		if (skb_is_gso(skb)) {
75562306a36Sopenharmony_ci			if (skb->inner_protocol)
75662306a36Sopenharmony_ci				return xfrm_output_gso(net, sk, skb);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci			skb_shinfo(skb)->gso_type |= SKB_GSO_ESP;
75962306a36Sopenharmony_ci			goto out;
76062306a36Sopenharmony_ci		}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci		if (x->xso.dev && x->xso.dev->features & NETIF_F_HW_ESP_TX_CSUM)
76362306a36Sopenharmony_ci			goto out;
76462306a36Sopenharmony_ci	} else {
76562306a36Sopenharmony_ci		if (skb_is_gso(skb))
76662306a36Sopenharmony_ci			return xfrm_output_gso(net, sk, skb);
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL) {
77062306a36Sopenharmony_ci		err = skb_checksum_help(skb);
77162306a36Sopenharmony_ci		if (err) {
77262306a36Sopenharmony_ci			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
77362306a36Sopenharmony_ci			kfree_skb(skb);
77462306a36Sopenharmony_ci			return err;
77562306a36Sopenharmony_ci		}
77662306a36Sopenharmony_ci	}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ciout:
77962306a36Sopenharmony_ci	return xfrm_output2(net, sk, skb);
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_output);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_cistatic int xfrm4_tunnel_check_size(struct sk_buff *skb)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	int mtu, ret = 0;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE)
78862306a36Sopenharmony_ci		goto out;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->ignore_df)
79162306a36Sopenharmony_ci		goto out;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	mtu = dst_mtu(skb_dst(skb));
79462306a36Sopenharmony_ci	if ((!skb_is_gso(skb) && skb->len > mtu) ||
79562306a36Sopenharmony_ci	    (skb_is_gso(skb) &&
79662306a36Sopenharmony_ci	     !skb_gso_validate_network_len(skb, ip_skb_dst_mtu(skb->sk, skb)))) {
79762306a36Sopenharmony_ci		skb->protocol = htons(ETH_P_IP);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci		if (skb->sk)
80062306a36Sopenharmony_ci			xfrm_local_error(skb, mtu);
80162306a36Sopenharmony_ci		else
80262306a36Sopenharmony_ci			icmp_send(skb, ICMP_DEST_UNREACH,
80362306a36Sopenharmony_ci				  ICMP_FRAG_NEEDED, htonl(mtu));
80462306a36Sopenharmony_ci		ret = -EMSGSIZE;
80562306a36Sopenharmony_ci	}
80662306a36Sopenharmony_ciout:
80762306a36Sopenharmony_ci	return ret;
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_cistatic int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	int err;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	if (x->outer_mode.encap == XFRM_MODE_BEET &&
81562306a36Sopenharmony_ci	    ip_is_fragment(ip_hdr(skb))) {
81662306a36Sopenharmony_ci		net_warn_ratelimited("BEET mode doesn't support inner IPv4 fragments\n");
81762306a36Sopenharmony_ci		return -EAFNOSUPPORT;
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	err = xfrm4_tunnel_check_size(skb);
82162306a36Sopenharmony_ci	if (err)
82262306a36Sopenharmony_ci		return err;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	XFRM_MODE_SKB_CB(skb)->protocol = ip_hdr(skb)->protocol;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	xfrm4_extract_header(skb);
82762306a36Sopenharmony_ci	return 0;
82862306a36Sopenharmony_ci}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
83162306a36Sopenharmony_cistatic int xfrm6_tunnel_check_size(struct sk_buff *skb)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	int mtu, ret = 0;
83462306a36Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	if (skb->ignore_df)
83762306a36Sopenharmony_ci		goto out;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	mtu = dst_mtu(dst);
84062306a36Sopenharmony_ci	if (mtu < IPV6_MIN_MTU)
84162306a36Sopenharmony_ci		mtu = IPV6_MIN_MTU;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	if ((!skb_is_gso(skb) && skb->len > mtu) ||
84462306a36Sopenharmony_ci	    (skb_is_gso(skb) &&
84562306a36Sopenharmony_ci	     !skb_gso_validate_network_len(skb, ip6_skb_dst_mtu(skb)))) {
84662306a36Sopenharmony_ci		skb->dev = dst->dev;
84762306a36Sopenharmony_ci		skb->protocol = htons(ETH_P_IPV6);
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci		if (xfrm6_local_dontfrag(skb->sk))
85062306a36Sopenharmony_ci			ipv6_stub->xfrm6_local_rxpmtu(skb, mtu);
85162306a36Sopenharmony_ci		else if (skb->sk)
85262306a36Sopenharmony_ci			xfrm_local_error(skb, mtu);
85362306a36Sopenharmony_ci		else
85462306a36Sopenharmony_ci			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
85562306a36Sopenharmony_ci		ret = -EMSGSIZE;
85662306a36Sopenharmony_ci	}
85762306a36Sopenharmony_ciout:
85862306a36Sopenharmony_ci	return ret;
85962306a36Sopenharmony_ci}
86062306a36Sopenharmony_ci#endif
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cistatic int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb)
86362306a36Sopenharmony_ci{
86462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
86562306a36Sopenharmony_ci	int err;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	err = xfrm6_tunnel_check_size(skb);
86862306a36Sopenharmony_ci	if (err)
86962306a36Sopenharmony_ci		return err;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	XFRM_MODE_SKB_CB(skb)->protocol = ipv6_hdr(skb)->nexthdr;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	xfrm6_extract_header(skb);
87462306a36Sopenharmony_ci	return 0;
87562306a36Sopenharmony_ci#else
87662306a36Sopenharmony_ci	WARN_ON_ONCE(1);
87762306a36Sopenharmony_ci	return -EAFNOSUPPORT;
87862306a36Sopenharmony_ci#endif
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	switch (skb->protocol) {
88462306a36Sopenharmony_ci	case htons(ETH_P_IP):
88562306a36Sopenharmony_ci		return xfrm4_extract_output(x, skb);
88662306a36Sopenharmony_ci	case htons(ETH_P_IPV6):
88762306a36Sopenharmony_ci		return xfrm6_extract_output(x, skb);
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	return -EAFNOSUPPORT;
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_civoid xfrm_local_error(struct sk_buff *skb, int mtu)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	unsigned int proto;
89662306a36Sopenharmony_ci	struct xfrm_state_afinfo *afinfo;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IP))
89962306a36Sopenharmony_ci		proto = AF_INET;
90062306a36Sopenharmony_ci	else if (skb->protocol == htons(ETH_P_IPV6) &&
90162306a36Sopenharmony_ci		 skb->sk->sk_family == AF_INET6)
90262306a36Sopenharmony_ci		proto = AF_INET6;
90362306a36Sopenharmony_ci	else
90462306a36Sopenharmony_ci		return;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	afinfo = xfrm_state_get_afinfo(proto);
90762306a36Sopenharmony_ci	if (afinfo) {
90862306a36Sopenharmony_ci		afinfo->local_error(skb, mtu);
90962306a36Sopenharmony_ci		rcu_read_unlock();
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xfrm_local_error);
913