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