18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C)2002 USAGI/WIDE Project 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Mitsuru KANDA @USAGI : IPv6 Support 88c2ecf20Sopenharmony_ci * Kazunori MIYAZAWA @USAGI : 98c2ecf20Sopenharmony_ci * Kunihiro Ishiguro <kunihiro@ipinfusion.com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This file is derived from net/ipv4/esp.c 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "IPv6: " fmt 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <crypto/aead.h> 178c2ecf20Sopenharmony_ci#include <crypto/authenc.h> 188c2ecf20Sopenharmony_ci#include <linux/err.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <net/ip.h> 218c2ecf20Sopenharmony_ci#include <net/xfrm.h> 228c2ecf20Sopenharmony_ci#include <net/esp.h> 238c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 248c2ecf20Sopenharmony_ci#include <linux/kernel.h> 258c2ecf20Sopenharmony_ci#include <linux/pfkeyv2.h> 268c2ecf20Sopenharmony_ci#include <linux/random.h> 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 298c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 308c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 318c2ecf20Sopenharmony_ci#include <net/icmp.h> 328c2ecf20Sopenharmony_ci#include <net/ipv6.h> 338c2ecf20Sopenharmony_ci#include <net/protocol.h> 348c2ecf20Sopenharmony_ci#include <net/udp.h> 358c2ecf20Sopenharmony_ci#include <linux/icmpv6.h> 368c2ecf20Sopenharmony_ci#include <net/tcp.h> 378c2ecf20Sopenharmony_ci#include <net/espintcp.h> 388c2ecf20Sopenharmony_ci#include <net/inet6_hashtables.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include <linux/highmem.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct esp_skb_cb { 438c2ecf20Sopenharmony_ci struct xfrm_skb_cb xfrm; 448c2ecf20Sopenharmony_ci void *tmp; 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistruct esp_output_extra { 488c2ecf20Sopenharmony_ci __be32 seqhi; 498c2ecf20Sopenharmony_ci u32 esphoff; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define ESP_SKB_CB(__skb) ((struct esp_skb_cb *)&((__skb)->cb[0])) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 558c2ecf20Sopenharmony_ci * Allocate an AEAD request structure with extra space for SG and IV. 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * For alignment considerations the upper 32 bits of the sequence number are 588c2ecf20Sopenharmony_ci * placed at the front, if present. Followed by the IV, the request and finally 598c2ecf20Sopenharmony_ci * the SG list. 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * TODO: Use spare space in skb for this where possible. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_cistatic void *esp_alloc_tmp(struct crypto_aead *aead, int nfrags, int seqihlen) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci unsigned int len; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci len = seqihlen; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci len += crypto_aead_ivsize(aead); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (len) { 728c2ecf20Sopenharmony_ci len += crypto_aead_alignmask(aead) & 738c2ecf20Sopenharmony_ci ~(crypto_tfm_ctx_alignment() - 1); 748c2ecf20Sopenharmony_ci len = ALIGN(len, crypto_tfm_ctx_alignment()); 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci len += sizeof(struct aead_request) + crypto_aead_reqsize(aead); 788c2ecf20Sopenharmony_ci len = ALIGN(len, __alignof__(struct scatterlist)); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci len += sizeof(struct scatterlist) * nfrags; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return kmalloc(len, GFP_ATOMIC); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic inline void *esp_tmp_extra(void *tmp) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci return PTR_ALIGN(tmp, __alignof__(struct esp_output_extra)); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic inline u8 *esp_tmp_iv(struct crypto_aead *aead, void *tmp, int seqhilen) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci return crypto_aead_ivsize(aead) ? 938c2ecf20Sopenharmony_ci PTR_ALIGN((u8 *)tmp + seqhilen, 948c2ecf20Sopenharmony_ci crypto_aead_alignmask(aead) + 1) : tmp + seqhilen; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic inline struct aead_request *esp_tmp_req(struct crypto_aead *aead, u8 *iv) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct aead_request *req; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci req = (void *)PTR_ALIGN(iv + crypto_aead_ivsize(aead), 1028c2ecf20Sopenharmony_ci crypto_tfm_ctx_alignment()); 1038c2ecf20Sopenharmony_ci aead_request_set_tfm(req, aead); 1048c2ecf20Sopenharmony_ci return req; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic inline struct scatterlist *esp_req_sg(struct crypto_aead *aead, 1088c2ecf20Sopenharmony_ci struct aead_request *req) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci return (void *)ALIGN((unsigned long)(req + 1) + 1118c2ecf20Sopenharmony_ci crypto_aead_reqsize(aead), 1128c2ecf20Sopenharmony_ci __alignof__(struct scatterlist)); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void esp_ssg_unref(struct xfrm_state *x, void *tmp) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct esp_output_extra *extra = esp_tmp_extra(tmp); 1188c2ecf20Sopenharmony_ci struct crypto_aead *aead = x->data; 1198c2ecf20Sopenharmony_ci int extralen = 0; 1208c2ecf20Sopenharmony_ci u8 *iv; 1218c2ecf20Sopenharmony_ci struct aead_request *req; 1228c2ecf20Sopenharmony_ci struct scatterlist *sg; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (x->props.flags & XFRM_STATE_ESN) 1258c2ecf20Sopenharmony_ci extralen += sizeof(*extra); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci iv = esp_tmp_iv(aead, tmp, extralen); 1288c2ecf20Sopenharmony_ci req = esp_tmp_req(aead, iv); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* Unref skb_frag_pages in the src scatterlist if necessary. 1318c2ecf20Sopenharmony_ci * Skip the first sg which comes from skb->data. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci if (req->src != req->dst) 1348c2ecf20Sopenharmony_ci for (sg = sg_next(req->src); sg; sg = sg_next(sg)) 1358c2ecf20Sopenharmony_ci put_page(sg_page(sg)); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci#ifdef CONFIG_INET6_ESPINTCP 1398c2ecf20Sopenharmony_cistruct esp_tcp_sk { 1408c2ecf20Sopenharmony_ci struct sock *sk; 1418c2ecf20Sopenharmony_ci struct rcu_head rcu; 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void esp_free_tcp_sk(struct rcu_head *head) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct esp_tcp_sk *esk = container_of(head, struct esp_tcp_sk, rcu); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci sock_put(esk->sk); 1498c2ecf20Sopenharmony_ci kfree(esk); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic struct sock *esp6_find_tcp_sk(struct xfrm_state *x) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct xfrm_encap_tmpl *encap = x->encap; 1558c2ecf20Sopenharmony_ci struct esp_tcp_sk *esk; 1568c2ecf20Sopenharmony_ci __be16 sport, dport; 1578c2ecf20Sopenharmony_ci struct sock *nsk; 1588c2ecf20Sopenharmony_ci struct sock *sk; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci sk = rcu_dereference(x->encap_sk); 1618c2ecf20Sopenharmony_ci if (sk && sk->sk_state == TCP_ESTABLISHED) 1628c2ecf20Sopenharmony_ci return sk; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci spin_lock_bh(&x->lock); 1658c2ecf20Sopenharmony_ci sport = encap->encap_sport; 1668c2ecf20Sopenharmony_ci dport = encap->encap_dport; 1678c2ecf20Sopenharmony_ci nsk = rcu_dereference_protected(x->encap_sk, 1688c2ecf20Sopenharmony_ci lockdep_is_held(&x->lock)); 1698c2ecf20Sopenharmony_ci if (sk && sk == nsk) { 1708c2ecf20Sopenharmony_ci esk = kmalloc(sizeof(*esk), GFP_ATOMIC); 1718c2ecf20Sopenharmony_ci if (!esk) { 1728c2ecf20Sopenharmony_ci spin_unlock_bh(&x->lock); 1738c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci RCU_INIT_POINTER(x->encap_sk, NULL); 1768c2ecf20Sopenharmony_ci esk->sk = sk; 1778c2ecf20Sopenharmony_ci call_rcu(&esk->rcu, esp_free_tcp_sk); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci spin_unlock_bh(&x->lock); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci sk = __inet6_lookup_established(xs_net(x), &tcp_hashinfo, &x->id.daddr.in6, 1828c2ecf20Sopenharmony_ci dport, &x->props.saddr.in6, ntohs(sport), 0, 0); 1838c2ecf20Sopenharmony_ci if (!sk) 1848c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (!tcp_is_ulp_esp(sk)) { 1878c2ecf20Sopenharmony_ci sock_put(sk); 1888c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci spin_lock_bh(&x->lock); 1928c2ecf20Sopenharmony_ci nsk = rcu_dereference_protected(x->encap_sk, 1938c2ecf20Sopenharmony_ci lockdep_is_held(&x->lock)); 1948c2ecf20Sopenharmony_ci if (encap->encap_sport != sport || 1958c2ecf20Sopenharmony_ci encap->encap_dport != dport) { 1968c2ecf20Sopenharmony_ci sock_put(sk); 1978c2ecf20Sopenharmony_ci sk = nsk ?: ERR_PTR(-EREMCHG); 1988c2ecf20Sopenharmony_ci } else if (sk == nsk) { 1998c2ecf20Sopenharmony_ci sock_put(sk); 2008c2ecf20Sopenharmony_ci } else { 2018c2ecf20Sopenharmony_ci rcu_assign_pointer(x->encap_sk, sk); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci spin_unlock_bh(&x->lock); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return sk; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int esp_output_tcp_finish(struct xfrm_state *x, struct sk_buff *skb) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct sock *sk; 2118c2ecf20Sopenharmony_ci int err; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci rcu_read_lock(); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci sk = esp6_find_tcp_sk(x); 2168c2ecf20Sopenharmony_ci err = PTR_ERR_OR_ZERO(sk); 2178c2ecf20Sopenharmony_ci if (err) 2188c2ecf20Sopenharmony_ci goto out; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci bh_lock_sock(sk); 2218c2ecf20Sopenharmony_ci if (sock_owned_by_user(sk)) 2228c2ecf20Sopenharmony_ci err = espintcp_queue_out(sk, skb); 2238c2ecf20Sopenharmony_ci else 2248c2ecf20Sopenharmony_ci err = espintcp_push_skb(sk, skb); 2258c2ecf20Sopenharmony_ci bh_unlock_sock(sk); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ciout: 2288c2ecf20Sopenharmony_ci rcu_read_unlock(); 2298c2ecf20Sopenharmony_ci return err; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic int esp_output_tcp_encap_cb(struct net *net, struct sock *sk, 2338c2ecf20Sopenharmony_ci struct sk_buff *skb) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 2368c2ecf20Sopenharmony_ci struct xfrm_state *x = dst->xfrm; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return esp_output_tcp_finish(x, skb); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int esp_output_tail_tcp(struct xfrm_state *x, struct sk_buff *skb) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci int err; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci local_bh_disable(); 2468c2ecf20Sopenharmony_ci err = xfrm_trans_queue_net(xs_net(x), skb, esp_output_tcp_encap_cb); 2478c2ecf20Sopenharmony_ci local_bh_enable(); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* EINPROGRESS just happens to do the right thing. It 2508c2ecf20Sopenharmony_ci * actually means that the skb has been consumed and 2518c2ecf20Sopenharmony_ci * isn't coming back. 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_ci return err ?: -EINPROGRESS; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci#else 2568c2ecf20Sopenharmony_cistatic int esp_output_tail_tcp(struct xfrm_state *x, struct sk_buff *skb) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci kfree_skb(skb); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci#endif 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic void esp_output_encap_csum(struct sk_buff *skb) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci /* UDP encap with IPv6 requires a valid checksum */ 2678c2ecf20Sopenharmony_ci if (*skb_mac_header(skb) == IPPROTO_UDP) { 2688c2ecf20Sopenharmony_ci struct udphdr *uh = udp_hdr(skb); 2698c2ecf20Sopenharmony_ci struct ipv6hdr *ip6h = ipv6_hdr(skb); 2708c2ecf20Sopenharmony_ci int len = ntohs(uh->len); 2718c2ecf20Sopenharmony_ci unsigned int offset = skb_transport_offset(skb); 2728c2ecf20Sopenharmony_ci __wsum csum = skb_checksum(skb, offset, skb->len - offset, 0); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci uh->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, 2758c2ecf20Sopenharmony_ci len, IPPROTO_UDP, csum); 2768c2ecf20Sopenharmony_ci if (uh->check == 0) 2778c2ecf20Sopenharmony_ci uh->check = CSUM_MANGLED_0; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic void esp_output_done(struct crypto_async_request *base, int err) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct sk_buff *skb = base->data; 2848c2ecf20Sopenharmony_ci struct xfrm_offload *xo = xfrm_offload(skb); 2858c2ecf20Sopenharmony_ci void *tmp; 2868c2ecf20Sopenharmony_ci struct xfrm_state *x; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (xo && (xo->flags & XFRM_DEV_RESUME)) { 2898c2ecf20Sopenharmony_ci struct sec_path *sp = skb_sec_path(skb); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci x = sp->xvec[sp->len - 1]; 2928c2ecf20Sopenharmony_ci } else { 2938c2ecf20Sopenharmony_ci x = skb_dst(skb)->xfrm; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci tmp = ESP_SKB_CB(skb)->tmp; 2978c2ecf20Sopenharmony_ci esp_ssg_unref(x, tmp); 2988c2ecf20Sopenharmony_ci kfree(tmp); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci esp_output_encap_csum(skb); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (xo && (xo->flags & XFRM_DEV_RESUME)) { 3038c2ecf20Sopenharmony_ci if (err) { 3048c2ecf20Sopenharmony_ci XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR); 3058c2ecf20Sopenharmony_ci kfree_skb(skb); 3068c2ecf20Sopenharmony_ci return; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci skb_push(skb, skb->data - skb_mac_header(skb)); 3108c2ecf20Sopenharmony_ci secpath_reset(skb); 3118c2ecf20Sopenharmony_ci xfrm_dev_resume(skb); 3128c2ecf20Sopenharmony_ci } else { 3138c2ecf20Sopenharmony_ci if (!err && 3148c2ecf20Sopenharmony_ci x->encap && x->encap->encap_type == TCP_ENCAP_ESPINTCP) 3158c2ecf20Sopenharmony_ci esp_output_tail_tcp(x, skb); 3168c2ecf20Sopenharmony_ci else 3178c2ecf20Sopenharmony_ci xfrm_output_resume(skb->sk, skb, err); 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci/* Move ESP header back into place. */ 3228c2ecf20Sopenharmony_cistatic void esp_restore_header(struct sk_buff *skb, unsigned int offset) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct ip_esp_hdr *esph = (void *)(skb->data + offset); 3258c2ecf20Sopenharmony_ci void *tmp = ESP_SKB_CB(skb)->tmp; 3268c2ecf20Sopenharmony_ci __be32 *seqhi = esp_tmp_extra(tmp); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci esph->seq_no = esph->spi; 3298c2ecf20Sopenharmony_ci esph->spi = *seqhi; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic void esp_output_restore_header(struct sk_buff *skb) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci void *tmp = ESP_SKB_CB(skb)->tmp; 3358c2ecf20Sopenharmony_ci struct esp_output_extra *extra = esp_tmp_extra(tmp); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci esp_restore_header(skb, skb_transport_offset(skb) + extra->esphoff - 3388c2ecf20Sopenharmony_ci sizeof(__be32)); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic struct ip_esp_hdr *esp_output_set_esn(struct sk_buff *skb, 3428c2ecf20Sopenharmony_ci struct xfrm_state *x, 3438c2ecf20Sopenharmony_ci struct ip_esp_hdr *esph, 3448c2ecf20Sopenharmony_ci struct esp_output_extra *extra) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci /* For ESN we move the header forward by 4 bytes to 3478c2ecf20Sopenharmony_ci * accomodate the high bits. We will move it back after 3488c2ecf20Sopenharmony_ci * encryption. 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_ci if ((x->props.flags & XFRM_STATE_ESN)) { 3518c2ecf20Sopenharmony_ci __u32 seqhi; 3528c2ecf20Sopenharmony_ci struct xfrm_offload *xo = xfrm_offload(skb); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (xo) 3558c2ecf20Sopenharmony_ci seqhi = xo->seq.hi; 3568c2ecf20Sopenharmony_ci else 3578c2ecf20Sopenharmony_ci seqhi = XFRM_SKB_CB(skb)->seq.output.hi; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci extra->esphoff = (unsigned char *)esph - 3608c2ecf20Sopenharmony_ci skb_transport_header(skb); 3618c2ecf20Sopenharmony_ci esph = (struct ip_esp_hdr *)((unsigned char *)esph - 4); 3628c2ecf20Sopenharmony_ci extra->seqhi = esph->spi; 3638c2ecf20Sopenharmony_ci esph->seq_no = htonl(seqhi); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci esph->spi = x->id.spi; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return esph; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic void esp_output_done_esn(struct crypto_async_request *base, int err) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct sk_buff *skb = base->data; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci esp_output_restore_header(skb); 3768c2ecf20Sopenharmony_ci esp_output_done(base, err); 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic struct ip_esp_hdr *esp6_output_udp_encap(struct sk_buff *skb, 3808c2ecf20Sopenharmony_ci int encap_type, 3818c2ecf20Sopenharmony_ci struct esp_info *esp, 3828c2ecf20Sopenharmony_ci __be16 sport, 3838c2ecf20Sopenharmony_ci __be16 dport) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct udphdr *uh; 3868c2ecf20Sopenharmony_ci __be32 *udpdata32; 3878c2ecf20Sopenharmony_ci unsigned int len; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci len = skb->len + esp->tailen - skb_transport_offset(skb); 3908c2ecf20Sopenharmony_ci if (len > U16_MAX) 3918c2ecf20Sopenharmony_ci return ERR_PTR(-EMSGSIZE); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci uh = (struct udphdr *)esp->esph; 3948c2ecf20Sopenharmony_ci uh->source = sport; 3958c2ecf20Sopenharmony_ci uh->dest = dport; 3968c2ecf20Sopenharmony_ci uh->len = htons(len); 3978c2ecf20Sopenharmony_ci uh->check = 0; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci *skb_mac_header(skb) = IPPROTO_UDP; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (encap_type == UDP_ENCAP_ESPINUDP_NON_IKE) { 4028c2ecf20Sopenharmony_ci udpdata32 = (__be32 *)(uh + 1); 4038c2ecf20Sopenharmony_ci udpdata32[0] = udpdata32[1] = 0; 4048c2ecf20Sopenharmony_ci return (struct ip_esp_hdr *)(udpdata32 + 2); 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return (struct ip_esp_hdr *)(uh + 1); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci#ifdef CONFIG_INET6_ESPINTCP 4118c2ecf20Sopenharmony_cistatic struct ip_esp_hdr *esp6_output_tcp_encap(struct xfrm_state *x, 4128c2ecf20Sopenharmony_ci struct sk_buff *skb, 4138c2ecf20Sopenharmony_ci struct esp_info *esp) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci __be16 *lenp = (void *)esp->esph; 4168c2ecf20Sopenharmony_ci struct ip_esp_hdr *esph; 4178c2ecf20Sopenharmony_ci unsigned int len; 4188c2ecf20Sopenharmony_ci struct sock *sk; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci len = skb->len + esp->tailen - skb_transport_offset(skb); 4218c2ecf20Sopenharmony_ci if (len > IP_MAX_MTU) 4228c2ecf20Sopenharmony_ci return ERR_PTR(-EMSGSIZE); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci rcu_read_lock(); 4258c2ecf20Sopenharmony_ci sk = esp6_find_tcp_sk(x); 4268c2ecf20Sopenharmony_ci rcu_read_unlock(); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (IS_ERR(sk)) 4298c2ecf20Sopenharmony_ci return ERR_CAST(sk); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci *lenp = htons(len); 4328c2ecf20Sopenharmony_ci esph = (struct ip_esp_hdr *)(lenp + 1); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return esph; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci#else 4378c2ecf20Sopenharmony_cistatic struct ip_esp_hdr *esp6_output_tcp_encap(struct xfrm_state *x, 4388c2ecf20Sopenharmony_ci struct sk_buff *skb, 4398c2ecf20Sopenharmony_ci struct esp_info *esp) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci return ERR_PTR(-EOPNOTSUPP); 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci#endif 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic int esp6_output_encap(struct xfrm_state *x, struct sk_buff *skb, 4468c2ecf20Sopenharmony_ci struct esp_info *esp) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct xfrm_encap_tmpl *encap = x->encap; 4498c2ecf20Sopenharmony_ci struct ip_esp_hdr *esph; 4508c2ecf20Sopenharmony_ci __be16 sport, dport; 4518c2ecf20Sopenharmony_ci int encap_type; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci spin_lock_bh(&x->lock); 4548c2ecf20Sopenharmony_ci sport = encap->encap_sport; 4558c2ecf20Sopenharmony_ci dport = encap->encap_dport; 4568c2ecf20Sopenharmony_ci encap_type = encap->encap_type; 4578c2ecf20Sopenharmony_ci spin_unlock_bh(&x->lock); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci switch (encap_type) { 4608c2ecf20Sopenharmony_ci default: 4618c2ecf20Sopenharmony_ci case UDP_ENCAP_ESPINUDP: 4628c2ecf20Sopenharmony_ci case UDP_ENCAP_ESPINUDP_NON_IKE: 4638c2ecf20Sopenharmony_ci esph = esp6_output_udp_encap(skb, encap_type, esp, sport, dport); 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci case TCP_ENCAP_ESPINTCP: 4668c2ecf20Sopenharmony_ci esph = esp6_output_tcp_encap(x, skb, esp); 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (IS_ERR(esph)) 4718c2ecf20Sopenharmony_ci return PTR_ERR(esph); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci esp->esph = esph; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return 0; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ciint esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci u8 *tail; 4818c2ecf20Sopenharmony_ci int nfrags; 4828c2ecf20Sopenharmony_ci int esph_offset; 4838c2ecf20Sopenharmony_ci struct page *page; 4848c2ecf20Sopenharmony_ci struct sk_buff *trailer; 4858c2ecf20Sopenharmony_ci int tailen = esp->tailen; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (x->encap) { 4888c2ecf20Sopenharmony_ci int err = esp6_output_encap(x, skb, esp); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (err < 0) 4918c2ecf20Sopenharmony_ci return err; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (ALIGN(tailen, L1_CACHE_BYTES) > PAGE_SIZE || 4958c2ecf20Sopenharmony_ci ALIGN(skb->data_len, L1_CACHE_BYTES) > PAGE_SIZE) 4968c2ecf20Sopenharmony_ci goto cow; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (!skb_cloned(skb)) { 4998c2ecf20Sopenharmony_ci if (tailen <= skb_tailroom(skb)) { 5008c2ecf20Sopenharmony_ci nfrags = 1; 5018c2ecf20Sopenharmony_ci trailer = skb; 5028c2ecf20Sopenharmony_ci tail = skb_tail_pointer(trailer); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci goto skip_cow; 5058c2ecf20Sopenharmony_ci } else if ((skb_shinfo(skb)->nr_frags < MAX_SKB_FRAGS) 5068c2ecf20Sopenharmony_ci && !skb_has_frag_list(skb)) { 5078c2ecf20Sopenharmony_ci int allocsize; 5088c2ecf20Sopenharmony_ci struct sock *sk = skb->sk; 5098c2ecf20Sopenharmony_ci struct page_frag *pfrag = &x->xfrag; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci esp->inplace = false; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci allocsize = ALIGN(tailen, L1_CACHE_BYTES); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci spin_lock_bh(&x->lock); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (unlikely(!skb_page_frag_refill(allocsize, pfrag, GFP_ATOMIC))) { 5188c2ecf20Sopenharmony_ci spin_unlock_bh(&x->lock); 5198c2ecf20Sopenharmony_ci goto cow; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci page = pfrag->page; 5238c2ecf20Sopenharmony_ci get_page(page); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci tail = page_address(page) + pfrag->offset; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci esp_output_fill_trailer(tail, esp->tfclen, esp->plen, esp->proto); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci nfrags = skb_shinfo(skb)->nr_frags; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci __skb_fill_page_desc(skb, nfrags, page, pfrag->offset, 5328c2ecf20Sopenharmony_ci tailen); 5338c2ecf20Sopenharmony_ci skb_shinfo(skb)->nr_frags = ++nfrags; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci pfrag->offset = pfrag->offset + allocsize; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci spin_unlock_bh(&x->lock); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci nfrags++; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci skb->len += tailen; 5428c2ecf20Sopenharmony_ci skb->data_len += tailen; 5438c2ecf20Sopenharmony_ci skb->truesize += tailen; 5448c2ecf20Sopenharmony_ci if (sk && sk_fullsock(sk)) 5458c2ecf20Sopenharmony_ci refcount_add(tailen, &sk->sk_wmem_alloc); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci goto out; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cicow: 5528c2ecf20Sopenharmony_ci esph_offset = (unsigned char *)esp->esph - skb_transport_header(skb); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci nfrags = skb_cow_data(skb, tailen, &trailer); 5558c2ecf20Sopenharmony_ci if (nfrags < 0) 5568c2ecf20Sopenharmony_ci goto out; 5578c2ecf20Sopenharmony_ci tail = skb_tail_pointer(trailer); 5588c2ecf20Sopenharmony_ci esp->esph = (struct ip_esp_hdr *)(skb_transport_header(skb) + esph_offset); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ciskip_cow: 5618c2ecf20Sopenharmony_ci esp_output_fill_trailer(tail, esp->tfclen, esp->plen, esp->proto); 5628c2ecf20Sopenharmony_ci pskb_put(skb, trailer, tailen); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ciout: 5658c2ecf20Sopenharmony_ci return nfrags; 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(esp6_output_head); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ciint esp6_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci u8 *iv; 5728c2ecf20Sopenharmony_ci int alen; 5738c2ecf20Sopenharmony_ci void *tmp; 5748c2ecf20Sopenharmony_ci int ivlen; 5758c2ecf20Sopenharmony_ci int assoclen; 5768c2ecf20Sopenharmony_ci int extralen; 5778c2ecf20Sopenharmony_ci struct page *page; 5788c2ecf20Sopenharmony_ci struct ip_esp_hdr *esph; 5798c2ecf20Sopenharmony_ci struct aead_request *req; 5808c2ecf20Sopenharmony_ci struct crypto_aead *aead; 5818c2ecf20Sopenharmony_ci struct scatterlist *sg, *dsg; 5828c2ecf20Sopenharmony_ci struct esp_output_extra *extra; 5838c2ecf20Sopenharmony_ci int err = -ENOMEM; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci assoclen = sizeof(struct ip_esp_hdr); 5868c2ecf20Sopenharmony_ci extralen = 0; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (x->props.flags & XFRM_STATE_ESN) { 5898c2ecf20Sopenharmony_ci extralen += sizeof(*extra); 5908c2ecf20Sopenharmony_ci assoclen += sizeof(__be32); 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci aead = x->data; 5948c2ecf20Sopenharmony_ci alen = crypto_aead_authsize(aead); 5958c2ecf20Sopenharmony_ci ivlen = crypto_aead_ivsize(aead); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci tmp = esp_alloc_tmp(aead, esp->nfrags + 2, extralen); 5988c2ecf20Sopenharmony_ci if (!tmp) 5998c2ecf20Sopenharmony_ci goto error; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci extra = esp_tmp_extra(tmp); 6028c2ecf20Sopenharmony_ci iv = esp_tmp_iv(aead, tmp, extralen); 6038c2ecf20Sopenharmony_ci req = esp_tmp_req(aead, iv); 6048c2ecf20Sopenharmony_ci sg = esp_req_sg(aead, req); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci if (esp->inplace) 6078c2ecf20Sopenharmony_ci dsg = sg; 6088c2ecf20Sopenharmony_ci else 6098c2ecf20Sopenharmony_ci dsg = &sg[esp->nfrags]; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci esph = esp_output_set_esn(skb, x, esp->esph, extra); 6128c2ecf20Sopenharmony_ci esp->esph = esph; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci sg_init_table(sg, esp->nfrags); 6158c2ecf20Sopenharmony_ci err = skb_to_sgvec(skb, sg, 6168c2ecf20Sopenharmony_ci (unsigned char *)esph - skb->data, 6178c2ecf20Sopenharmony_ci assoclen + ivlen + esp->clen + alen); 6188c2ecf20Sopenharmony_ci if (unlikely(err < 0)) 6198c2ecf20Sopenharmony_ci goto error_free; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (!esp->inplace) { 6228c2ecf20Sopenharmony_ci int allocsize; 6238c2ecf20Sopenharmony_ci struct page_frag *pfrag = &x->xfrag; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci allocsize = ALIGN(skb->data_len, L1_CACHE_BYTES); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci spin_lock_bh(&x->lock); 6288c2ecf20Sopenharmony_ci if (unlikely(!skb_page_frag_refill(allocsize, pfrag, GFP_ATOMIC))) { 6298c2ecf20Sopenharmony_ci spin_unlock_bh(&x->lock); 6308c2ecf20Sopenharmony_ci goto error_free; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci skb_shinfo(skb)->nr_frags = 1; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci page = pfrag->page; 6368c2ecf20Sopenharmony_ci get_page(page); 6378c2ecf20Sopenharmony_ci /* replace page frags in skb with new page */ 6388c2ecf20Sopenharmony_ci __skb_fill_page_desc(skb, 0, page, pfrag->offset, skb->data_len); 6398c2ecf20Sopenharmony_ci pfrag->offset = pfrag->offset + allocsize; 6408c2ecf20Sopenharmony_ci spin_unlock_bh(&x->lock); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci sg_init_table(dsg, skb_shinfo(skb)->nr_frags + 1); 6438c2ecf20Sopenharmony_ci err = skb_to_sgvec(skb, dsg, 6448c2ecf20Sopenharmony_ci (unsigned char *)esph - skb->data, 6458c2ecf20Sopenharmony_ci assoclen + ivlen + esp->clen + alen); 6468c2ecf20Sopenharmony_ci if (unlikely(err < 0)) 6478c2ecf20Sopenharmony_ci goto error_free; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if ((x->props.flags & XFRM_STATE_ESN)) 6518c2ecf20Sopenharmony_ci aead_request_set_callback(req, 0, esp_output_done_esn, skb); 6528c2ecf20Sopenharmony_ci else 6538c2ecf20Sopenharmony_ci aead_request_set_callback(req, 0, esp_output_done, skb); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci aead_request_set_crypt(req, sg, dsg, ivlen + esp->clen, iv); 6568c2ecf20Sopenharmony_ci aead_request_set_ad(req, assoclen); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci memset(iv, 0, ivlen); 6598c2ecf20Sopenharmony_ci memcpy(iv + ivlen - min(ivlen, 8), (u8 *)&esp->seqno + 8 - min(ivlen, 8), 6608c2ecf20Sopenharmony_ci min(ivlen, 8)); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci ESP_SKB_CB(skb)->tmp = tmp; 6638c2ecf20Sopenharmony_ci err = crypto_aead_encrypt(req); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci switch (err) { 6668c2ecf20Sopenharmony_ci case -EINPROGRESS: 6678c2ecf20Sopenharmony_ci goto error; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci case -ENOSPC: 6708c2ecf20Sopenharmony_ci err = NET_XMIT_DROP; 6718c2ecf20Sopenharmony_ci break; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci case 0: 6748c2ecf20Sopenharmony_ci if ((x->props.flags & XFRM_STATE_ESN)) 6758c2ecf20Sopenharmony_ci esp_output_restore_header(skb); 6768c2ecf20Sopenharmony_ci esp_output_encap_csum(skb); 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (sg != dsg) 6808c2ecf20Sopenharmony_ci esp_ssg_unref(x, tmp); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci if (!err && x->encap && x->encap->encap_type == TCP_ENCAP_ESPINTCP) 6838c2ecf20Sopenharmony_ci err = esp_output_tail_tcp(x, skb); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cierror_free: 6868c2ecf20Sopenharmony_ci kfree(tmp); 6878c2ecf20Sopenharmony_cierror: 6888c2ecf20Sopenharmony_ci return err; 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(esp6_output_tail); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic int esp6_output(struct xfrm_state *x, struct sk_buff *skb) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci int alen; 6958c2ecf20Sopenharmony_ci int blksize; 6968c2ecf20Sopenharmony_ci struct ip_esp_hdr *esph; 6978c2ecf20Sopenharmony_ci struct crypto_aead *aead; 6988c2ecf20Sopenharmony_ci struct esp_info esp; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci esp.inplace = true; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci esp.proto = *skb_mac_header(skb); 7038c2ecf20Sopenharmony_ci *skb_mac_header(skb) = IPPROTO_ESP; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* skb is pure payload to encrypt */ 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci aead = x->data; 7088c2ecf20Sopenharmony_ci alen = crypto_aead_authsize(aead); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci esp.tfclen = 0; 7118c2ecf20Sopenharmony_ci if (x->tfcpad) { 7128c2ecf20Sopenharmony_ci struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); 7138c2ecf20Sopenharmony_ci u32 padto; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci padto = min(x->tfcpad, xfrm_state_mtu(x, dst->child_mtu_cached)); 7168c2ecf20Sopenharmony_ci if (skb->len < padto) 7178c2ecf20Sopenharmony_ci esp.tfclen = padto - skb->len; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci blksize = ALIGN(crypto_aead_blocksize(aead), 4); 7208c2ecf20Sopenharmony_ci esp.clen = ALIGN(skb->len + 2 + esp.tfclen, blksize); 7218c2ecf20Sopenharmony_ci esp.plen = esp.clen - skb->len - esp.tfclen; 7228c2ecf20Sopenharmony_ci esp.tailen = esp.tfclen + esp.plen + alen; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci esp.esph = ip_esp_hdr(skb); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci esp.nfrags = esp6_output_head(x, skb, &esp); 7278c2ecf20Sopenharmony_ci if (esp.nfrags < 0) 7288c2ecf20Sopenharmony_ci return esp.nfrags; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci esph = esp.esph; 7318c2ecf20Sopenharmony_ci esph->spi = x->id.spi; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); 7348c2ecf20Sopenharmony_ci esp.seqno = cpu_to_be64(XFRM_SKB_CB(skb)->seq.output.low + 7358c2ecf20Sopenharmony_ci ((u64)XFRM_SKB_CB(skb)->seq.output.hi << 32)); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci skb_push(skb, -skb_network_offset(skb)); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci return esp6_output_tail(x, skb, &esp); 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_cistatic inline int esp_remove_trailer(struct sk_buff *skb) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci struct xfrm_state *x = xfrm_input_state(skb); 7458c2ecf20Sopenharmony_ci struct xfrm_offload *xo = xfrm_offload(skb); 7468c2ecf20Sopenharmony_ci struct crypto_aead *aead = x->data; 7478c2ecf20Sopenharmony_ci int alen, hlen, elen; 7488c2ecf20Sopenharmony_ci int padlen, trimlen; 7498c2ecf20Sopenharmony_ci __wsum csumdiff; 7508c2ecf20Sopenharmony_ci u8 nexthdr[2]; 7518c2ecf20Sopenharmony_ci int ret; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci alen = crypto_aead_authsize(aead); 7548c2ecf20Sopenharmony_ci hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); 7558c2ecf20Sopenharmony_ci elen = skb->len - hlen; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (xo && (xo->flags & XFRM_ESP_NO_TRAILER)) { 7588c2ecf20Sopenharmony_ci ret = xo->proto; 7598c2ecf20Sopenharmony_ci goto out; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci ret = skb_copy_bits(skb, skb->len - alen - 2, nexthdr, 2); 7638c2ecf20Sopenharmony_ci BUG_ON(ret); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci ret = -EINVAL; 7668c2ecf20Sopenharmony_ci padlen = nexthdr[0]; 7678c2ecf20Sopenharmony_ci if (padlen + 2 + alen >= elen) { 7688c2ecf20Sopenharmony_ci net_dbg_ratelimited("ipsec esp packet is garbage padlen=%d, elen=%d\n", 7698c2ecf20Sopenharmony_ci padlen + 2, elen - alen); 7708c2ecf20Sopenharmony_ci goto out; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci trimlen = alen + padlen + 2; 7748c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_COMPLETE) { 7758c2ecf20Sopenharmony_ci csumdiff = skb_checksum(skb, skb->len - trimlen, trimlen, 0); 7768c2ecf20Sopenharmony_ci skb->csum = csum_block_sub(skb->csum, csumdiff, 7778c2ecf20Sopenharmony_ci skb->len - trimlen); 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci ret = pskb_trim(skb, skb->len - trimlen); 7808c2ecf20Sopenharmony_ci if (unlikely(ret)) 7818c2ecf20Sopenharmony_ci return ret; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci ret = nexthdr[1]; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ciout: 7868c2ecf20Sopenharmony_ci return ret; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ciint esp6_input_done2(struct sk_buff *skb, int err) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct xfrm_state *x = xfrm_input_state(skb); 7928c2ecf20Sopenharmony_ci struct xfrm_offload *xo = xfrm_offload(skb); 7938c2ecf20Sopenharmony_ci struct crypto_aead *aead = x->data; 7948c2ecf20Sopenharmony_ci int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); 7958c2ecf20Sopenharmony_ci int hdr_len = skb_network_header_len(skb); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (!xo || (xo && !(xo->flags & CRYPTO_DONE))) 7988c2ecf20Sopenharmony_ci kfree(ESP_SKB_CB(skb)->tmp); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci if (unlikely(err)) 8018c2ecf20Sopenharmony_ci goto out; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci err = esp_remove_trailer(skb); 8048c2ecf20Sopenharmony_ci if (unlikely(err < 0)) 8058c2ecf20Sopenharmony_ci goto out; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (x->encap) { 8088c2ecf20Sopenharmony_ci const struct ipv6hdr *ip6h = ipv6_hdr(skb); 8098c2ecf20Sopenharmony_ci int offset = skb_network_offset(skb) + sizeof(*ip6h); 8108c2ecf20Sopenharmony_ci struct xfrm_encap_tmpl *encap = x->encap; 8118c2ecf20Sopenharmony_ci u8 nexthdr = ip6h->nexthdr; 8128c2ecf20Sopenharmony_ci __be16 frag_off, source; 8138c2ecf20Sopenharmony_ci struct udphdr *uh; 8148c2ecf20Sopenharmony_ci struct tcphdr *th; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off); 8178c2ecf20Sopenharmony_ci if (offset == -1) { 8188c2ecf20Sopenharmony_ci err = -EINVAL; 8198c2ecf20Sopenharmony_ci goto out; 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci uh = (void *)(skb->data + offset); 8238c2ecf20Sopenharmony_ci th = (void *)(skb->data + offset); 8248c2ecf20Sopenharmony_ci hdr_len += offset; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci switch (x->encap->encap_type) { 8278c2ecf20Sopenharmony_ci case TCP_ENCAP_ESPINTCP: 8288c2ecf20Sopenharmony_ci source = th->source; 8298c2ecf20Sopenharmony_ci break; 8308c2ecf20Sopenharmony_ci case UDP_ENCAP_ESPINUDP: 8318c2ecf20Sopenharmony_ci case UDP_ENCAP_ESPINUDP_NON_IKE: 8328c2ecf20Sopenharmony_ci source = uh->source; 8338c2ecf20Sopenharmony_ci break; 8348c2ecf20Sopenharmony_ci default: 8358c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 8368c2ecf20Sopenharmony_ci err = -EINVAL; 8378c2ecf20Sopenharmony_ci goto out; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci /* 8418c2ecf20Sopenharmony_ci * 1) if the NAT-T peer's IP or port changed then 8428c2ecf20Sopenharmony_ci * advertize the change to the keying daemon. 8438c2ecf20Sopenharmony_ci * This is an inbound SA, so just compare 8448c2ecf20Sopenharmony_ci * SRC ports. 8458c2ecf20Sopenharmony_ci */ 8468c2ecf20Sopenharmony_ci if (!ipv6_addr_equal(&ip6h->saddr, &x->props.saddr.in6) || 8478c2ecf20Sopenharmony_ci source != encap->encap_sport) { 8488c2ecf20Sopenharmony_ci xfrm_address_t ipaddr; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci memcpy(&ipaddr.a6, &ip6h->saddr.s6_addr, sizeof(ipaddr.a6)); 8518c2ecf20Sopenharmony_ci km_new_mapping(x, &ipaddr, source); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci /* XXX: perhaps add an extra 8548c2ecf20Sopenharmony_ci * policy check here, to see 8558c2ecf20Sopenharmony_ci * if we should allow or 8568c2ecf20Sopenharmony_ci * reject a packet from a 8578c2ecf20Sopenharmony_ci * different source 8588c2ecf20Sopenharmony_ci * address/port. 8598c2ecf20Sopenharmony_ci */ 8608c2ecf20Sopenharmony_ci } 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* 8638c2ecf20Sopenharmony_ci * 2) ignore UDP/TCP checksums in case 8648c2ecf20Sopenharmony_ci * of NAT-T in Transport Mode, or 8658c2ecf20Sopenharmony_ci * perform other post-processing fixes 8668c2ecf20Sopenharmony_ci * as per draft-ietf-ipsec-udp-encaps-06, 8678c2ecf20Sopenharmony_ci * section 3.1.2 8688c2ecf20Sopenharmony_ci */ 8698c2ecf20Sopenharmony_ci if (x->props.mode == XFRM_MODE_TRANSPORT) 8708c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci skb_postpull_rcsum(skb, skb_network_header(skb), 8748c2ecf20Sopenharmony_ci skb_network_header_len(skb)); 8758c2ecf20Sopenharmony_ci skb_pull_rcsum(skb, hlen); 8768c2ecf20Sopenharmony_ci if (x->props.mode == XFRM_MODE_TUNNEL) 8778c2ecf20Sopenharmony_ci skb_reset_transport_header(skb); 8788c2ecf20Sopenharmony_ci else 8798c2ecf20Sopenharmony_ci skb_set_transport_header(skb, -hdr_len); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci /* RFC4303: Drop dummy packets without any error */ 8828c2ecf20Sopenharmony_ci if (err == IPPROTO_NONE) 8838c2ecf20Sopenharmony_ci err = -EINVAL; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ciout: 8868c2ecf20Sopenharmony_ci return err; 8878c2ecf20Sopenharmony_ci} 8888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(esp6_input_done2); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic void esp_input_done(struct crypto_async_request *base, int err) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci struct sk_buff *skb = base->data; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci xfrm_input_resume(skb, esp6_input_done2(skb, err)); 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_cistatic void esp_input_restore_header(struct sk_buff *skb) 8988c2ecf20Sopenharmony_ci{ 8998c2ecf20Sopenharmony_ci esp_restore_header(skb, 0); 9008c2ecf20Sopenharmony_ci __skb_pull(skb, 4); 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cistatic void esp_input_set_header(struct sk_buff *skb, __be32 *seqhi) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct xfrm_state *x = xfrm_input_state(skb); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* For ESN we move the header forward by 4 bytes to 9088c2ecf20Sopenharmony_ci * accomodate the high bits. We will move it back after 9098c2ecf20Sopenharmony_ci * decryption. 9108c2ecf20Sopenharmony_ci */ 9118c2ecf20Sopenharmony_ci if ((x->props.flags & XFRM_STATE_ESN)) { 9128c2ecf20Sopenharmony_ci struct ip_esp_hdr *esph = skb_push(skb, 4); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci *seqhi = esph->spi; 9158c2ecf20Sopenharmony_ci esph->spi = esph->seq_no; 9168c2ecf20Sopenharmony_ci esph->seq_no = XFRM_SKB_CB(skb)->seq.input.hi; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_cistatic void esp_input_done_esn(struct crypto_async_request *base, int err) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci struct sk_buff *skb = base->data; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci esp_input_restore_header(skb); 9258c2ecf20Sopenharmony_ci esp_input_done(base, err); 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_cistatic int esp6_input(struct xfrm_state *x, struct sk_buff *skb) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci struct crypto_aead *aead = x->data; 9318c2ecf20Sopenharmony_ci struct aead_request *req; 9328c2ecf20Sopenharmony_ci struct sk_buff *trailer; 9338c2ecf20Sopenharmony_ci int ivlen = crypto_aead_ivsize(aead); 9348c2ecf20Sopenharmony_ci int elen = skb->len - sizeof(struct ip_esp_hdr) - ivlen; 9358c2ecf20Sopenharmony_ci int nfrags; 9368c2ecf20Sopenharmony_ci int assoclen; 9378c2ecf20Sopenharmony_ci int seqhilen; 9388c2ecf20Sopenharmony_ci int ret = 0; 9398c2ecf20Sopenharmony_ci void *tmp; 9408c2ecf20Sopenharmony_ci __be32 *seqhi; 9418c2ecf20Sopenharmony_ci u8 *iv; 9428c2ecf20Sopenharmony_ci struct scatterlist *sg; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + ivlen)) { 9458c2ecf20Sopenharmony_ci ret = -EINVAL; 9468c2ecf20Sopenharmony_ci goto out; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci if (elen <= 0) { 9508c2ecf20Sopenharmony_ci ret = -EINVAL; 9518c2ecf20Sopenharmony_ci goto out; 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci assoclen = sizeof(struct ip_esp_hdr); 9558c2ecf20Sopenharmony_ci seqhilen = 0; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (x->props.flags & XFRM_STATE_ESN) { 9588c2ecf20Sopenharmony_ci seqhilen += sizeof(__be32); 9598c2ecf20Sopenharmony_ci assoclen += seqhilen; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci if (!skb_cloned(skb)) { 9638c2ecf20Sopenharmony_ci if (!skb_is_nonlinear(skb)) { 9648c2ecf20Sopenharmony_ci nfrags = 1; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci goto skip_cow; 9678c2ecf20Sopenharmony_ci } else if (!skb_has_frag_list(skb)) { 9688c2ecf20Sopenharmony_ci nfrags = skb_shinfo(skb)->nr_frags; 9698c2ecf20Sopenharmony_ci nfrags++; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci goto skip_cow; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci nfrags = skb_cow_data(skb, 0, &trailer); 9768c2ecf20Sopenharmony_ci if (nfrags < 0) { 9778c2ecf20Sopenharmony_ci ret = -EINVAL; 9788c2ecf20Sopenharmony_ci goto out; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ciskip_cow: 9828c2ecf20Sopenharmony_ci ret = -ENOMEM; 9838c2ecf20Sopenharmony_ci tmp = esp_alloc_tmp(aead, nfrags, seqhilen); 9848c2ecf20Sopenharmony_ci if (!tmp) 9858c2ecf20Sopenharmony_ci goto out; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci ESP_SKB_CB(skb)->tmp = tmp; 9888c2ecf20Sopenharmony_ci seqhi = esp_tmp_extra(tmp); 9898c2ecf20Sopenharmony_ci iv = esp_tmp_iv(aead, tmp, seqhilen); 9908c2ecf20Sopenharmony_ci req = esp_tmp_req(aead, iv); 9918c2ecf20Sopenharmony_ci sg = esp_req_sg(aead, req); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci esp_input_set_header(skb, seqhi); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci sg_init_table(sg, nfrags); 9968c2ecf20Sopenharmony_ci ret = skb_to_sgvec(skb, sg, 0, skb->len); 9978c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) { 9988c2ecf20Sopenharmony_ci kfree(tmp); 9998c2ecf20Sopenharmony_ci goto out; 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci if ((x->props.flags & XFRM_STATE_ESN)) 10058c2ecf20Sopenharmony_ci aead_request_set_callback(req, 0, esp_input_done_esn, skb); 10068c2ecf20Sopenharmony_ci else 10078c2ecf20Sopenharmony_ci aead_request_set_callback(req, 0, esp_input_done, skb); 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci aead_request_set_crypt(req, sg, sg, elen + ivlen, iv); 10108c2ecf20Sopenharmony_ci aead_request_set_ad(req, assoclen); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci ret = crypto_aead_decrypt(req); 10138c2ecf20Sopenharmony_ci if (ret == -EINPROGRESS) 10148c2ecf20Sopenharmony_ci goto out; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci if ((x->props.flags & XFRM_STATE_ESN)) 10178c2ecf20Sopenharmony_ci esp_input_restore_header(skb); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci ret = esp6_input_done2(skb, ret); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ciout: 10228c2ecf20Sopenharmony_ci return ret; 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic int esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 10268c2ecf20Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 10278c2ecf20Sopenharmony_ci{ 10288c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 10298c2ecf20Sopenharmony_ci const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data; 10308c2ecf20Sopenharmony_ci struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data + offset); 10318c2ecf20Sopenharmony_ci struct xfrm_state *x; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci if (type != ICMPV6_PKT_TOOBIG && 10348c2ecf20Sopenharmony_ci type != NDISC_REDIRECT) 10358c2ecf20Sopenharmony_ci return 0; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, 10388c2ecf20Sopenharmony_ci esph->spi, IPPROTO_ESP, AF_INET6); 10398c2ecf20Sopenharmony_ci if (!x) 10408c2ecf20Sopenharmony_ci return 0; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci if (type == NDISC_REDIRECT) 10438c2ecf20Sopenharmony_ci ip6_redirect(skb, net, skb->dev->ifindex, 0, 10448c2ecf20Sopenharmony_ci sock_net_uid(net, NULL)); 10458c2ecf20Sopenharmony_ci else 10468c2ecf20Sopenharmony_ci ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); 10478c2ecf20Sopenharmony_ci xfrm_state_put(x); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci return 0; 10508c2ecf20Sopenharmony_ci} 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_cistatic void esp6_destroy(struct xfrm_state *x) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci struct crypto_aead *aead = x->data; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci if (!aead) 10578c2ecf20Sopenharmony_ci return; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci crypto_free_aead(aead); 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_cistatic int esp_init_aead(struct xfrm_state *x) 10638c2ecf20Sopenharmony_ci{ 10648c2ecf20Sopenharmony_ci char aead_name[CRYPTO_MAX_ALG_NAME]; 10658c2ecf20Sopenharmony_ci struct crypto_aead *aead; 10668c2ecf20Sopenharmony_ci int err; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci err = -ENAMETOOLONG; 10698c2ecf20Sopenharmony_ci if (snprintf(aead_name, CRYPTO_MAX_ALG_NAME, "%s(%s)", 10708c2ecf20Sopenharmony_ci x->geniv, x->aead->alg_name) >= CRYPTO_MAX_ALG_NAME) 10718c2ecf20Sopenharmony_ci goto error; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci aead = crypto_alloc_aead(aead_name, 0, 0); 10748c2ecf20Sopenharmony_ci err = PTR_ERR(aead); 10758c2ecf20Sopenharmony_ci if (IS_ERR(aead)) 10768c2ecf20Sopenharmony_ci goto error; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci x->data = aead; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci err = crypto_aead_setkey(aead, x->aead->alg_key, 10818c2ecf20Sopenharmony_ci (x->aead->alg_key_len + 7) / 8); 10828c2ecf20Sopenharmony_ci if (err) 10838c2ecf20Sopenharmony_ci goto error; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci err = crypto_aead_setauthsize(aead, x->aead->alg_icv_len / 8); 10868c2ecf20Sopenharmony_ci if (err) 10878c2ecf20Sopenharmony_ci goto error; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_cierror: 10908c2ecf20Sopenharmony_ci return err; 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_cistatic int esp_init_authenc(struct xfrm_state *x) 10948c2ecf20Sopenharmony_ci{ 10958c2ecf20Sopenharmony_ci struct crypto_aead *aead; 10968c2ecf20Sopenharmony_ci struct crypto_authenc_key_param *param; 10978c2ecf20Sopenharmony_ci struct rtattr *rta; 10988c2ecf20Sopenharmony_ci char *key; 10998c2ecf20Sopenharmony_ci char *p; 11008c2ecf20Sopenharmony_ci char authenc_name[CRYPTO_MAX_ALG_NAME]; 11018c2ecf20Sopenharmony_ci unsigned int keylen; 11028c2ecf20Sopenharmony_ci int err; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci err = -EINVAL; 11058c2ecf20Sopenharmony_ci if (!x->ealg) 11068c2ecf20Sopenharmony_ci goto error; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci err = -ENAMETOOLONG; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci if ((x->props.flags & XFRM_STATE_ESN)) { 11118c2ecf20Sopenharmony_ci if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME, 11128c2ecf20Sopenharmony_ci "%s%sauthencesn(%s,%s)%s", 11138c2ecf20Sopenharmony_ci x->geniv ?: "", x->geniv ? "(" : "", 11148c2ecf20Sopenharmony_ci x->aalg ? x->aalg->alg_name : "digest_null", 11158c2ecf20Sopenharmony_ci x->ealg->alg_name, 11168c2ecf20Sopenharmony_ci x->geniv ? ")" : "") >= CRYPTO_MAX_ALG_NAME) 11178c2ecf20Sopenharmony_ci goto error; 11188c2ecf20Sopenharmony_ci } else { 11198c2ecf20Sopenharmony_ci if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME, 11208c2ecf20Sopenharmony_ci "%s%sauthenc(%s,%s)%s", 11218c2ecf20Sopenharmony_ci x->geniv ?: "", x->geniv ? "(" : "", 11228c2ecf20Sopenharmony_ci x->aalg ? x->aalg->alg_name : "digest_null", 11238c2ecf20Sopenharmony_ci x->ealg->alg_name, 11248c2ecf20Sopenharmony_ci x->geniv ? ")" : "") >= CRYPTO_MAX_ALG_NAME) 11258c2ecf20Sopenharmony_ci goto error; 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci aead = crypto_alloc_aead(authenc_name, 0, 0); 11298c2ecf20Sopenharmony_ci err = PTR_ERR(aead); 11308c2ecf20Sopenharmony_ci if (IS_ERR(aead)) 11318c2ecf20Sopenharmony_ci goto error; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci x->data = aead; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci keylen = (x->aalg ? (x->aalg->alg_key_len + 7) / 8 : 0) + 11368c2ecf20Sopenharmony_ci (x->ealg->alg_key_len + 7) / 8 + RTA_SPACE(sizeof(*param)); 11378c2ecf20Sopenharmony_ci err = -ENOMEM; 11388c2ecf20Sopenharmony_ci key = kmalloc(keylen, GFP_KERNEL); 11398c2ecf20Sopenharmony_ci if (!key) 11408c2ecf20Sopenharmony_ci goto error; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci p = key; 11438c2ecf20Sopenharmony_ci rta = (void *)p; 11448c2ecf20Sopenharmony_ci rta->rta_type = CRYPTO_AUTHENC_KEYA_PARAM; 11458c2ecf20Sopenharmony_ci rta->rta_len = RTA_LENGTH(sizeof(*param)); 11468c2ecf20Sopenharmony_ci param = RTA_DATA(rta); 11478c2ecf20Sopenharmony_ci p += RTA_SPACE(sizeof(*param)); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if (x->aalg) { 11508c2ecf20Sopenharmony_ci struct xfrm_algo_desc *aalg_desc; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci memcpy(p, x->aalg->alg_key, (x->aalg->alg_key_len + 7) / 8); 11538c2ecf20Sopenharmony_ci p += (x->aalg->alg_key_len + 7) / 8; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0); 11568c2ecf20Sopenharmony_ci BUG_ON(!aalg_desc); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci err = -EINVAL; 11598c2ecf20Sopenharmony_ci if (aalg_desc->uinfo.auth.icv_fullbits / 8 != 11608c2ecf20Sopenharmony_ci crypto_aead_authsize(aead)) { 11618c2ecf20Sopenharmony_ci pr_info("ESP: %s digestsize %u != %hu\n", 11628c2ecf20Sopenharmony_ci x->aalg->alg_name, 11638c2ecf20Sopenharmony_ci crypto_aead_authsize(aead), 11648c2ecf20Sopenharmony_ci aalg_desc->uinfo.auth.icv_fullbits / 8); 11658c2ecf20Sopenharmony_ci goto free_key; 11668c2ecf20Sopenharmony_ci } 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci err = crypto_aead_setauthsize( 11698c2ecf20Sopenharmony_ci aead, x->aalg->alg_trunc_len / 8); 11708c2ecf20Sopenharmony_ci if (err) 11718c2ecf20Sopenharmony_ci goto free_key; 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci param->enckeylen = cpu_to_be32((x->ealg->alg_key_len + 7) / 8); 11758c2ecf20Sopenharmony_ci memcpy(p, x->ealg->alg_key, (x->ealg->alg_key_len + 7) / 8); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci err = crypto_aead_setkey(aead, key, keylen); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_cifree_key: 11808c2ecf20Sopenharmony_ci kfree(key); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_cierror: 11838c2ecf20Sopenharmony_ci return err; 11848c2ecf20Sopenharmony_ci} 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_cistatic int esp6_init_state(struct xfrm_state *x) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci struct crypto_aead *aead; 11898c2ecf20Sopenharmony_ci u32 align; 11908c2ecf20Sopenharmony_ci int err; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci x->data = NULL; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci if (x->aead) 11958c2ecf20Sopenharmony_ci err = esp_init_aead(x); 11968c2ecf20Sopenharmony_ci else 11978c2ecf20Sopenharmony_ci err = esp_init_authenc(x); 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci if (err) 12008c2ecf20Sopenharmony_ci goto error; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci aead = x->data; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci x->props.header_len = sizeof(struct ip_esp_hdr) + 12058c2ecf20Sopenharmony_ci crypto_aead_ivsize(aead); 12068c2ecf20Sopenharmony_ci switch (x->props.mode) { 12078c2ecf20Sopenharmony_ci case XFRM_MODE_BEET: 12088c2ecf20Sopenharmony_ci if (x->sel.family != AF_INET6) 12098c2ecf20Sopenharmony_ci x->props.header_len += IPV4_BEET_PHMAXLEN + 12108c2ecf20Sopenharmony_ci (sizeof(struct ipv6hdr) - sizeof(struct iphdr)); 12118c2ecf20Sopenharmony_ci break; 12128c2ecf20Sopenharmony_ci default: 12138c2ecf20Sopenharmony_ci case XFRM_MODE_TRANSPORT: 12148c2ecf20Sopenharmony_ci break; 12158c2ecf20Sopenharmony_ci case XFRM_MODE_TUNNEL: 12168c2ecf20Sopenharmony_ci x->props.header_len += sizeof(struct ipv6hdr); 12178c2ecf20Sopenharmony_ci break; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (x->encap) { 12218c2ecf20Sopenharmony_ci struct xfrm_encap_tmpl *encap = x->encap; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci switch (encap->encap_type) { 12248c2ecf20Sopenharmony_ci default: 12258c2ecf20Sopenharmony_ci err = -EINVAL; 12268c2ecf20Sopenharmony_ci goto error; 12278c2ecf20Sopenharmony_ci case UDP_ENCAP_ESPINUDP: 12288c2ecf20Sopenharmony_ci x->props.header_len += sizeof(struct udphdr); 12298c2ecf20Sopenharmony_ci break; 12308c2ecf20Sopenharmony_ci case UDP_ENCAP_ESPINUDP_NON_IKE: 12318c2ecf20Sopenharmony_ci x->props.header_len += sizeof(struct udphdr) + 2 * sizeof(u32); 12328c2ecf20Sopenharmony_ci break; 12338c2ecf20Sopenharmony_ci#ifdef CONFIG_INET6_ESPINTCP 12348c2ecf20Sopenharmony_ci case TCP_ENCAP_ESPINTCP: 12358c2ecf20Sopenharmony_ci /* only the length field, TCP encap is done by 12368c2ecf20Sopenharmony_ci * the socket 12378c2ecf20Sopenharmony_ci */ 12388c2ecf20Sopenharmony_ci x->props.header_len += 2; 12398c2ecf20Sopenharmony_ci break; 12408c2ecf20Sopenharmony_ci#endif 12418c2ecf20Sopenharmony_ci } 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci align = ALIGN(crypto_aead_blocksize(aead), 4); 12458c2ecf20Sopenharmony_ci x->props.trailer_len = align + 1 + crypto_aead_authsize(aead); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_cierror: 12488c2ecf20Sopenharmony_ci return err; 12498c2ecf20Sopenharmony_ci} 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_cistatic int esp6_rcv_cb(struct sk_buff *skb, int err) 12528c2ecf20Sopenharmony_ci{ 12538c2ecf20Sopenharmony_ci return 0; 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_cistatic const struct xfrm_type esp6_type = { 12578c2ecf20Sopenharmony_ci .description = "ESP6", 12588c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 12598c2ecf20Sopenharmony_ci .proto = IPPROTO_ESP, 12608c2ecf20Sopenharmony_ci .flags = XFRM_TYPE_REPLAY_PROT, 12618c2ecf20Sopenharmony_ci .init_state = esp6_init_state, 12628c2ecf20Sopenharmony_ci .destructor = esp6_destroy, 12638c2ecf20Sopenharmony_ci .input = esp6_input, 12648c2ecf20Sopenharmony_ci .output = esp6_output, 12658c2ecf20Sopenharmony_ci .hdr_offset = xfrm6_find_1stfragopt, 12668c2ecf20Sopenharmony_ci}; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_cistatic struct xfrm6_protocol esp6_protocol = { 12698c2ecf20Sopenharmony_ci .handler = xfrm6_rcv, 12708c2ecf20Sopenharmony_ci .input_handler = xfrm_input, 12718c2ecf20Sopenharmony_ci .cb_handler = esp6_rcv_cb, 12728c2ecf20Sopenharmony_ci .err_handler = esp6_err, 12738c2ecf20Sopenharmony_ci .priority = 0, 12748c2ecf20Sopenharmony_ci}; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_cistatic int __init esp6_init(void) 12778c2ecf20Sopenharmony_ci{ 12788c2ecf20Sopenharmony_ci if (xfrm_register_type(&esp6_type, AF_INET6) < 0) { 12798c2ecf20Sopenharmony_ci pr_info("%s: can't add xfrm type\n", __func__); 12808c2ecf20Sopenharmony_ci return -EAGAIN; 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci if (xfrm6_protocol_register(&esp6_protocol, IPPROTO_ESP) < 0) { 12838c2ecf20Sopenharmony_ci pr_info("%s: can't add protocol\n", __func__); 12848c2ecf20Sopenharmony_ci xfrm_unregister_type(&esp6_type, AF_INET6); 12858c2ecf20Sopenharmony_ci return -EAGAIN; 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci return 0; 12898c2ecf20Sopenharmony_ci} 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_cistatic void __exit esp6_fini(void) 12928c2ecf20Sopenharmony_ci{ 12938c2ecf20Sopenharmony_ci if (xfrm6_protocol_deregister(&esp6_protocol, IPPROTO_ESP) < 0) 12948c2ecf20Sopenharmony_ci pr_info("%s: can't remove protocol\n", __func__); 12958c2ecf20Sopenharmony_ci xfrm_unregister_type(&esp6_type, AF_INET6); 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_cimodule_init(esp6_init); 12998c2ecf20Sopenharmony_cimodule_exit(esp6_fini); 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 13028c2ecf20Sopenharmony_ciMODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_ESP); 1303