11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Copyright (C)2002 USAGI/WIDE Project 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 51da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 61da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 71da177e4SLinus Torvalds * (at your option) any later version. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 101da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 111da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 121da177e4SLinus Torvalds * GNU General Public License for more details. 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 151da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 161da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * Authors 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds * Mitsuru KANDA @USAGI : IPv6 Support 211da177e4SLinus Torvalds * Kazunori MIYAZAWA @USAGI : 221da177e4SLinus Torvalds * Kunihiro Ishiguro <kunihiro@ipinfusion.com> 231da177e4SLinus Torvalds * 241da177e4SLinus Torvalds * This file is derived from net/ipv4/ah.c. 251da177e4SLinus Torvalds */ 261da177e4SLinus Torvalds 27f3213831SJoe Perches #define pr_fmt(fmt) "IPv6: " fmt 28f3213831SJoe Perches 298631e9bdSSteffen Klassert #include <crypto/hash.h> 301da177e4SLinus Torvalds #include <linux/module.h> 315a0e3ad6STejun Heo #include <linux/slab.h> 321da177e4SLinus Torvalds #include <net/ip.h> 331da177e4SLinus Torvalds #include <net/ah.h> 341da177e4SLinus Torvalds #include <linux/crypto.h> 351da177e4SLinus Torvalds #include <linux/pfkeyv2.h> 361da177e4SLinus Torvalds #include <linux/string.h> 378631e9bdSSteffen Klassert #include <linux/scatterlist.h> 3881aded24SDavid S. Miller #include <net/ip6_route.h> 391da177e4SLinus Torvalds #include <net/icmp.h> 401da177e4SLinus Torvalds #include <net/ipv6.h> 4114c85021SArnaldo Carvalho de Melo #include <net/protocol.h> 421da177e4SLinus Torvalds #include <net/xfrm.h> 431da177e4SLinus Torvalds 448631e9bdSSteffen Klassert #define IPV6HDR_BASELEN 8 458631e9bdSSteffen Klassert 468631e9bdSSteffen Klassert struct tmp_ext { 478631e9bdSSteffen Klassert #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 488631e9bdSSteffen Klassert struct in6_addr saddr; 498631e9bdSSteffen Klassert #endif 508631e9bdSSteffen Klassert struct in6_addr daddr; 518631e9bdSSteffen Klassert char hdrs[0]; 528631e9bdSSteffen Klassert }; 538631e9bdSSteffen Klassert 548631e9bdSSteffen Klassert struct ah_skb_cb { 558631e9bdSSteffen Klassert struct xfrm_skb_cb xfrm; 568631e9bdSSteffen Klassert void *tmp; 578631e9bdSSteffen Klassert }; 588631e9bdSSteffen Klassert 598631e9bdSSteffen Klassert #define AH_SKB_CB(__skb) ((struct ah_skb_cb *)&((__skb)->cb[0])) 608631e9bdSSteffen Klassert 618631e9bdSSteffen Klassert static void *ah_alloc_tmp(struct crypto_ahash *ahash, int nfrags, 628631e9bdSSteffen Klassert unsigned int size) 638631e9bdSSteffen Klassert { 648631e9bdSSteffen Klassert unsigned int len; 658631e9bdSSteffen Klassert 668631e9bdSSteffen Klassert len = size + crypto_ahash_digestsize(ahash) + 678631e9bdSSteffen Klassert (crypto_ahash_alignmask(ahash) & 688631e9bdSSteffen Klassert ~(crypto_tfm_ctx_alignment() - 1)); 698631e9bdSSteffen Klassert 708631e9bdSSteffen Klassert len = ALIGN(len, crypto_tfm_ctx_alignment()); 718631e9bdSSteffen Klassert 728631e9bdSSteffen Klassert len += sizeof(struct ahash_request) + crypto_ahash_reqsize(ahash); 738631e9bdSSteffen Klassert len = ALIGN(len, __alignof__(struct scatterlist)); 748631e9bdSSteffen Klassert 758631e9bdSSteffen Klassert len += sizeof(struct scatterlist) * nfrags; 768631e9bdSSteffen Klassert 778631e9bdSSteffen Klassert return kmalloc(len, GFP_ATOMIC); 788631e9bdSSteffen Klassert } 798631e9bdSSteffen Klassert 808631e9bdSSteffen Klassert static inline struct tmp_ext *ah_tmp_ext(void *base) 818631e9bdSSteffen Klassert { 828631e9bdSSteffen Klassert return base + IPV6HDR_BASELEN; 838631e9bdSSteffen Klassert } 848631e9bdSSteffen Klassert 858631e9bdSSteffen Klassert static inline u8 *ah_tmp_auth(u8 *tmp, unsigned int offset) 868631e9bdSSteffen Klassert { 878631e9bdSSteffen Klassert return tmp + offset; 888631e9bdSSteffen Klassert } 898631e9bdSSteffen Klassert 908631e9bdSSteffen Klassert static inline u8 *ah_tmp_icv(struct crypto_ahash *ahash, void *tmp, 918631e9bdSSteffen Klassert unsigned int offset) 928631e9bdSSteffen Klassert { 938631e9bdSSteffen Klassert return PTR_ALIGN((u8 *)tmp + offset, crypto_ahash_alignmask(ahash) + 1); 948631e9bdSSteffen Klassert } 958631e9bdSSteffen Klassert 968631e9bdSSteffen Klassert static inline struct ahash_request *ah_tmp_req(struct crypto_ahash *ahash, 978631e9bdSSteffen Klassert u8 *icv) 988631e9bdSSteffen Klassert { 998631e9bdSSteffen Klassert struct ahash_request *req; 1008631e9bdSSteffen Klassert 1018631e9bdSSteffen Klassert req = (void *)PTR_ALIGN(icv + crypto_ahash_digestsize(ahash), 1028631e9bdSSteffen Klassert crypto_tfm_ctx_alignment()); 1038631e9bdSSteffen Klassert 1048631e9bdSSteffen Klassert ahash_request_set_tfm(req, ahash); 1058631e9bdSSteffen Klassert 1068631e9bdSSteffen Klassert return req; 1078631e9bdSSteffen Klassert } 1088631e9bdSSteffen Klassert 1098631e9bdSSteffen Klassert static inline struct scatterlist *ah_req_sg(struct crypto_ahash *ahash, 1108631e9bdSSteffen Klassert struct ahash_request *req) 1118631e9bdSSteffen Klassert { 1128631e9bdSSteffen Klassert return (void *)ALIGN((unsigned long)(req + 1) + 1138631e9bdSSteffen Klassert crypto_ahash_reqsize(ahash), 1148631e9bdSSteffen Klassert __alignof__(struct scatterlist)); 1158631e9bdSSteffen Klassert } 1168631e9bdSSteffen Klassert 117a50feda5SEric Dumazet static bool zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr) 1181da177e4SLinus Torvalds { 1191da177e4SLinus Torvalds u8 *opt = (u8 *)opthdr; 1201da177e4SLinus Torvalds int len = ipv6_optlen(opthdr); 1211da177e4SLinus Torvalds int off = 0; 1221da177e4SLinus Torvalds int optlen = 0; 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds off += 2; 1251da177e4SLinus Torvalds len -= 2; 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds while (len > 0) { 1281da177e4SLinus Torvalds 1291da177e4SLinus Torvalds switch (opt[off]) { 1301da177e4SLinus Torvalds 1311de5a71cSEldad Zack case IPV6_TLV_PAD1: 1321da177e4SLinus Torvalds optlen = 1; 1331da177e4SLinus Torvalds break; 1341da177e4SLinus Torvalds default: 1351da177e4SLinus Torvalds if (len < 2) 1361da177e4SLinus Torvalds goto bad; 1371da177e4SLinus Torvalds optlen = opt[off+1]+2; 1381da177e4SLinus Torvalds if (len < optlen) 1391da177e4SLinus Torvalds goto bad; 1401da177e4SLinus Torvalds if (opt[off] & 0x20) 1411da177e4SLinus Torvalds memset(&opt[off+2], 0, opt[off+1]); 1421da177e4SLinus Torvalds break; 1431da177e4SLinus Torvalds } 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds off += optlen; 1461da177e4SLinus Torvalds len -= optlen; 1471da177e4SLinus Torvalds } 1481da177e4SLinus Torvalds if (len == 0) 149a50feda5SEric Dumazet return true; 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds bad: 152a50feda5SEric Dumazet return false; 1531da177e4SLinus Torvalds } 1541da177e4SLinus Torvalds 15559fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 15627637df9SMasahide NAKAMURA /** 15727637df9SMasahide NAKAMURA * ipv6_rearrange_destopt - rearrange IPv6 destination options header 15827637df9SMasahide NAKAMURA * @iph: IPv6 header 15927637df9SMasahide NAKAMURA * @destopt: destionation options header 16027637df9SMasahide NAKAMURA */ 16127637df9SMasahide NAKAMURA static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *destopt) 16227637df9SMasahide NAKAMURA { 16327637df9SMasahide NAKAMURA u8 *opt = (u8 *)destopt; 16427637df9SMasahide NAKAMURA int len = ipv6_optlen(destopt); 16527637df9SMasahide NAKAMURA int off = 0; 16627637df9SMasahide NAKAMURA int optlen = 0; 16727637df9SMasahide NAKAMURA 16827637df9SMasahide NAKAMURA off += 2; 16927637df9SMasahide NAKAMURA len -= 2; 17027637df9SMasahide NAKAMURA 17127637df9SMasahide NAKAMURA while (len > 0) { 17227637df9SMasahide NAKAMURA 17327637df9SMasahide NAKAMURA switch (opt[off]) { 17427637df9SMasahide NAKAMURA 1751de5a71cSEldad Zack case IPV6_TLV_PAD1: 17627637df9SMasahide NAKAMURA optlen = 1; 17727637df9SMasahide NAKAMURA break; 17827637df9SMasahide NAKAMURA default: 17927637df9SMasahide NAKAMURA if (len < 2) 18027637df9SMasahide NAKAMURA goto bad; 18127637df9SMasahide NAKAMURA optlen = opt[off+1]+2; 18227637df9SMasahide NAKAMURA if (len < optlen) 18327637df9SMasahide NAKAMURA goto bad; 18427637df9SMasahide NAKAMURA 18527637df9SMasahide NAKAMURA /* Rearrange the source address in @iph and the 18627637df9SMasahide NAKAMURA * addresses in home address option for final source. 18727637df9SMasahide NAKAMURA * See 11.3.2 of RFC 3775 for details. 18827637df9SMasahide NAKAMURA */ 18927637df9SMasahide NAKAMURA if (opt[off] == IPV6_TLV_HAO) { 19027637df9SMasahide NAKAMURA struct in6_addr final_addr; 19127637df9SMasahide NAKAMURA struct ipv6_destopt_hao *hao; 19227637df9SMasahide NAKAMURA 19327637df9SMasahide NAKAMURA hao = (struct ipv6_destopt_hao *)&opt[off]; 19427637df9SMasahide NAKAMURA if (hao->length != sizeof(hao->addr)) { 195e87cc472SJoe Perches net_warn_ratelimited("destopt hao: invalid header length: %u\n", 196e87cc472SJoe Perches hao->length); 19727637df9SMasahide NAKAMURA goto bad; 19827637df9SMasahide NAKAMURA } 1994e3fd7a0SAlexey Dobriyan final_addr = hao->addr; 2004e3fd7a0SAlexey Dobriyan hao->addr = iph->saddr; 2014e3fd7a0SAlexey Dobriyan iph->saddr = final_addr; 20227637df9SMasahide NAKAMURA } 20327637df9SMasahide NAKAMURA break; 20427637df9SMasahide NAKAMURA } 20527637df9SMasahide NAKAMURA 20627637df9SMasahide NAKAMURA off += optlen; 20727637df9SMasahide NAKAMURA len -= optlen; 20827637df9SMasahide NAKAMURA } 209e731c248SYOSHIFUJI Hideaki /* Note: ok if len == 0 */ 21027637df9SMasahide NAKAMURA bad: 21127637df9SMasahide NAKAMURA return; 21227637df9SMasahide NAKAMURA } 213136ebf08SMasahide NAKAMURA #else 214136ebf08SMasahide NAKAMURA static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *destopt) {} 21527637df9SMasahide NAKAMURA #endif 21627637df9SMasahide NAKAMURA 2171da177e4SLinus Torvalds /** 2181da177e4SLinus Torvalds * ipv6_rearrange_rthdr - rearrange IPv6 routing header 2191da177e4SLinus Torvalds * @iph: IPv6 header 2201da177e4SLinus Torvalds * @rthdr: routing header 2211da177e4SLinus Torvalds * 2221da177e4SLinus Torvalds * Rearrange the destination address in @iph and the addresses in @rthdr 2231da177e4SLinus Torvalds * so that they appear in the order they will at the final destination. 2241da177e4SLinus Torvalds * See Appendix A2 of RFC 2402 for details. 2251da177e4SLinus Torvalds */ 2261da177e4SLinus Torvalds static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr) 2271da177e4SLinus Torvalds { 2281da177e4SLinus Torvalds int segments, segments_left; 2291da177e4SLinus Torvalds struct in6_addr *addrs; 2301da177e4SLinus Torvalds struct in6_addr final_addr; 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds segments_left = rthdr->segments_left; 2331da177e4SLinus Torvalds if (segments_left == 0) 2341da177e4SLinus Torvalds return; 2351da177e4SLinus Torvalds rthdr->segments_left = 0; 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds /* The value of rthdr->hdrlen has been verified either by the system 2381da177e4SLinus Torvalds * call if it is locally generated, or by ipv6_rthdr_rcv() for incoming 2391da177e4SLinus Torvalds * packets. So we can assume that it is even and that segments is 2401da177e4SLinus Torvalds * greater than or equal to segments_left. 2411da177e4SLinus Torvalds * 2421da177e4SLinus Torvalds * For the same reason we can assume that this option is of type 0. 2431da177e4SLinus Torvalds */ 2441da177e4SLinus Torvalds segments = rthdr->hdrlen >> 1; 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds addrs = ((struct rt0_hdr *)rthdr)->addr; 2474e3fd7a0SAlexey Dobriyan final_addr = addrs[segments - 1]; 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds addrs += segments - segments_left; 2501da177e4SLinus Torvalds memmove(addrs + 1, addrs, (segments_left - 1) * sizeof(*addrs)); 2511da177e4SLinus Torvalds 2524e3fd7a0SAlexey Dobriyan addrs[0] = iph->daddr; 2534e3fd7a0SAlexey Dobriyan iph->daddr = final_addr; 2541da177e4SLinus Torvalds } 2551da177e4SLinus Torvalds 25627637df9SMasahide NAKAMURA static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir) 2571da177e4SLinus Torvalds { 2581da177e4SLinus Torvalds union { 2591da177e4SLinus Torvalds struct ipv6hdr *iph; 2601da177e4SLinus Torvalds struct ipv6_opt_hdr *opth; 2611da177e4SLinus Torvalds struct ipv6_rt_hdr *rth; 2621da177e4SLinus Torvalds char *raw; 2631da177e4SLinus Torvalds } exthdr = { .iph = iph }; 2641da177e4SLinus Torvalds char *end = exthdr.raw + len; 2651da177e4SLinus Torvalds int nexthdr = iph->nexthdr; 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds exthdr.iph++; 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds while (exthdr.raw < end) { 2701da177e4SLinus Torvalds switch (nexthdr) { 27127637df9SMasahide NAKAMURA case NEXTHDR_DEST: 27227637df9SMasahide NAKAMURA if (dir == XFRM_POLICY_OUT) 27327637df9SMasahide NAKAMURA ipv6_rearrange_destopt(iph, exthdr.opth); 274e731c248SYOSHIFUJI Hideaki case NEXTHDR_HOP: 275e731c248SYOSHIFUJI Hideaki if (!zero_out_mutable_opts(exthdr.opth)) { 276e731c248SYOSHIFUJI Hideaki LIMIT_NETDEBUG( 277e731c248SYOSHIFUJI Hideaki KERN_WARNING "overrun %sopts\n", 278e731c248SYOSHIFUJI Hideaki nexthdr == NEXTHDR_HOP ? 279e731c248SYOSHIFUJI Hideaki "hop" : "dest"); 280e731c248SYOSHIFUJI Hideaki return -EINVAL; 281e731c248SYOSHIFUJI Hideaki } 282e731c248SYOSHIFUJI Hideaki break; 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds case NEXTHDR_ROUTING: 2851da177e4SLinus Torvalds ipv6_rearrange_rthdr(iph, exthdr.rth); 2861da177e4SLinus Torvalds break; 2871da177e4SLinus Torvalds 2881da177e4SLinus Torvalds default : 2891da177e4SLinus Torvalds return 0; 2901da177e4SLinus Torvalds } 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds nexthdr = exthdr.opth->nexthdr; 2931da177e4SLinus Torvalds exthdr.raw += ipv6_optlen(exthdr.opth); 2941da177e4SLinus Torvalds } 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds return 0; 2971da177e4SLinus Torvalds } 2981da177e4SLinus Torvalds 2998631e9bdSSteffen Klassert static void ah6_output_done(struct crypto_async_request *base, int err) 3008631e9bdSSteffen Klassert { 3018631e9bdSSteffen Klassert int extlen; 3028631e9bdSSteffen Klassert u8 *iph_base; 3038631e9bdSSteffen Klassert u8 *icv; 3048631e9bdSSteffen Klassert struct sk_buff *skb = base->data; 3058631e9bdSSteffen Klassert struct xfrm_state *x = skb_dst(skb)->xfrm; 3068631e9bdSSteffen Klassert struct ah_data *ahp = x->data; 3078631e9bdSSteffen Klassert struct ipv6hdr *top_iph = ipv6_hdr(skb); 3088631e9bdSSteffen Klassert struct ip_auth_hdr *ah = ip_auth_hdr(skb); 3098631e9bdSSteffen Klassert struct tmp_ext *iph_ext; 3108631e9bdSSteffen Klassert 3118631e9bdSSteffen Klassert extlen = skb_network_header_len(skb) - sizeof(struct ipv6hdr); 3128631e9bdSSteffen Klassert if (extlen) 3138631e9bdSSteffen Klassert extlen += sizeof(*iph_ext); 3148631e9bdSSteffen Klassert 3158631e9bdSSteffen Klassert iph_base = AH_SKB_CB(skb)->tmp; 3168631e9bdSSteffen Klassert iph_ext = ah_tmp_ext(iph_base); 3178631e9bdSSteffen Klassert icv = ah_tmp_icv(ahp->ahash, iph_ext, extlen); 3188631e9bdSSteffen Klassert 3198631e9bdSSteffen Klassert memcpy(ah->auth_data, icv, ahp->icv_trunc_len); 3208631e9bdSSteffen Klassert memcpy(top_iph, iph_base, IPV6HDR_BASELEN); 3218631e9bdSSteffen Klassert 3228631e9bdSSteffen Klassert if (extlen) { 3238631e9bdSSteffen Klassert #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 3248631e9bdSSteffen Klassert memcpy(&top_iph->saddr, iph_ext, extlen); 3258631e9bdSSteffen Klassert #else 3268631e9bdSSteffen Klassert memcpy(&top_iph->daddr, iph_ext, extlen); 3278631e9bdSSteffen Klassert #endif 3288631e9bdSSteffen Klassert } 3298631e9bdSSteffen Klassert 3308631e9bdSSteffen Klassert kfree(AH_SKB_CB(skb)->tmp); 3318631e9bdSSteffen Klassert xfrm_output_resume(skb, err); 3328631e9bdSSteffen Klassert } 3338631e9bdSSteffen Klassert 3341da177e4SLinus Torvalds static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) 3351da177e4SLinus Torvalds { 3361da177e4SLinus Torvalds int err; 3378631e9bdSSteffen Klassert int nfrags; 3381da177e4SLinus Torvalds int extlen; 3398631e9bdSSteffen Klassert u8 *iph_base; 3408631e9bdSSteffen Klassert u8 *icv; 3418631e9bdSSteffen Klassert u8 nexthdr; 3428631e9bdSSteffen Klassert struct sk_buff *trailer; 3438631e9bdSSteffen Klassert struct crypto_ahash *ahash; 3448631e9bdSSteffen Klassert struct ahash_request *req; 3458631e9bdSSteffen Klassert struct scatterlist *sg; 3461da177e4SLinus Torvalds struct ipv6hdr *top_iph; 3471da177e4SLinus Torvalds struct ip_auth_hdr *ah; 3481da177e4SLinus Torvalds struct ah_data *ahp; 3498631e9bdSSteffen Klassert struct tmp_ext *iph_ext; 3508631e9bdSSteffen Klassert 3518631e9bdSSteffen Klassert ahp = x->data; 3528631e9bdSSteffen Klassert ahash = ahp->ahash; 3538631e9bdSSteffen Klassert 3548631e9bdSSteffen Klassert if ((err = skb_cow_data(skb, 0, &trailer)) < 0) 3558631e9bdSSteffen Klassert goto out; 3568631e9bdSSteffen Klassert nfrags = err; 3571da177e4SLinus Torvalds 3587b277b1aSHerbert Xu skb_push(skb, -skb_network_offset(skb)); 3598631e9bdSSteffen Klassert extlen = skb_network_header_len(skb) - sizeof(struct ipv6hdr); 3608631e9bdSSteffen Klassert if (extlen) 3618631e9bdSSteffen Klassert extlen += sizeof(*iph_ext); 3628631e9bdSSteffen Klassert 3638631e9bdSSteffen Klassert err = -ENOMEM; 3648631e9bdSSteffen Klassert iph_base = ah_alloc_tmp(ahash, nfrags, IPV6HDR_BASELEN + extlen); 3658631e9bdSSteffen Klassert if (!iph_base) 3668631e9bdSSteffen Klassert goto out; 3678631e9bdSSteffen Klassert 3688631e9bdSSteffen Klassert iph_ext = ah_tmp_ext(iph_base); 3698631e9bdSSteffen Klassert icv = ah_tmp_icv(ahash, iph_ext, extlen); 3708631e9bdSSteffen Klassert req = ah_tmp_req(ahash, icv); 3718631e9bdSSteffen Klassert sg = ah_req_sg(ahash, req); 3728631e9bdSSteffen Klassert 3738631e9bdSSteffen Klassert ah = ip_auth_hdr(skb); 3748631e9bdSSteffen Klassert memset(ah->auth_data, 0, ahp->icv_trunc_len); 3758631e9bdSSteffen Klassert 376007f0211SHerbert Xu top_iph = ipv6_hdr(skb); 3771da177e4SLinus Torvalds top_iph->payload_len = htons(skb->len - sizeof(*top_iph)); 3781da177e4SLinus Torvalds 379007f0211SHerbert Xu nexthdr = *skb_mac_header(skb); 380007f0211SHerbert Xu *skb_mac_header(skb) = IPPROTO_AH; 3811da177e4SLinus Torvalds 3821da177e4SLinus Torvalds /* When there are no extension headers, we only need to save the first 3831da177e4SLinus Torvalds * 8 bytes of the base IP header. 3841da177e4SLinus Torvalds */ 3858631e9bdSSteffen Klassert memcpy(iph_base, top_iph, IPV6HDR_BASELEN); 3861da177e4SLinus Torvalds 3871da177e4SLinus Torvalds if (extlen) { 38859fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 3898631e9bdSSteffen Klassert memcpy(iph_ext, &top_iph->saddr, extlen); 390e731c248SYOSHIFUJI Hideaki #else 3918631e9bdSSteffen Klassert memcpy(iph_ext, &top_iph->daddr, extlen); 392e731c248SYOSHIFUJI Hideaki #endif 39327637df9SMasahide NAKAMURA err = ipv6_clear_mutable_options(top_iph, 3948631e9bdSSteffen Klassert extlen - sizeof(*iph_ext) + 39527637df9SMasahide NAKAMURA sizeof(*top_iph), 39627637df9SMasahide NAKAMURA XFRM_POLICY_OUT); 3971da177e4SLinus Torvalds if (err) 3988631e9bdSSteffen Klassert goto out_free; 3991da177e4SLinus Torvalds } 4001da177e4SLinus Torvalds 4011da177e4SLinus Torvalds ah->nexthdr = nexthdr; 4021da177e4SLinus Torvalds 4031da177e4SLinus Torvalds top_iph->priority = 0; 4041da177e4SLinus Torvalds top_iph->flow_lbl[0] = 0; 4051da177e4SLinus Torvalds top_iph->flow_lbl[1] = 0; 4061da177e4SLinus Torvalds top_iph->flow_lbl[2] = 0; 4071da177e4SLinus Torvalds top_iph->hop_limit = 0; 4081da177e4SLinus Torvalds 40987bdc48dSHerbert Xu ah->hdrlen = (XFRM_ALIGN8(sizeof(*ah) + ahp->icv_trunc_len) >> 2) - 2; 4101da177e4SLinus Torvalds 4111da177e4SLinus Torvalds ah->reserved = 0; 4121da177e4SLinus Torvalds ah->spi = x->id.spi; 4131ce3644aSSteffen Klassert ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); 414b7c6538cSHerbert Xu 4158631e9bdSSteffen Klassert sg_init_table(sg, nfrags); 4168631e9bdSSteffen Klassert skb_to_sgvec(skb, sg, 0, skb->len); 417b7c6538cSHerbert Xu 4188631e9bdSSteffen Klassert ahash_request_set_crypt(req, sg, icv, skb->len); 4198631e9bdSSteffen Klassert ahash_request_set_callback(req, 0, ah6_output_done, skb); 4201da177e4SLinus Torvalds 4218631e9bdSSteffen Klassert AH_SKB_CB(skb)->tmp = iph_base; 4228631e9bdSSteffen Klassert 4238631e9bdSSteffen Klassert err = crypto_ahash_digest(req); 4248631e9bdSSteffen Klassert if (err) { 4258631e9bdSSteffen Klassert if (err == -EINPROGRESS) 4268631e9bdSSteffen Klassert goto out; 4278631e9bdSSteffen Klassert 4288631e9bdSSteffen Klassert if (err == -EBUSY) 4298631e9bdSSteffen Klassert err = NET_XMIT_DROP; 4308631e9bdSSteffen Klassert goto out_free; 4311da177e4SLinus Torvalds } 4321da177e4SLinus Torvalds 4338631e9bdSSteffen Klassert memcpy(ah->auth_data, icv, ahp->icv_trunc_len); 4348631e9bdSSteffen Klassert memcpy(top_iph, iph_base, IPV6HDR_BASELEN); 4358631e9bdSSteffen Klassert 4368631e9bdSSteffen Klassert if (extlen) { 4378631e9bdSSteffen Klassert #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 4388631e9bdSSteffen Klassert memcpy(&top_iph->saddr, iph_ext, extlen); 4398631e9bdSSteffen Klassert #else 4408631e9bdSSteffen Klassert memcpy(&top_iph->daddr, iph_ext, extlen); 4418631e9bdSSteffen Klassert #endif 4428631e9bdSSteffen Klassert } 4438631e9bdSSteffen Klassert 4448631e9bdSSteffen Klassert out_free: 4458631e9bdSSteffen Klassert kfree(iph_base); 4468631e9bdSSteffen Klassert out: 4471da177e4SLinus Torvalds return err; 4481da177e4SLinus Torvalds } 4491da177e4SLinus Torvalds 4508631e9bdSSteffen Klassert static void ah6_input_done(struct crypto_async_request *base, int err) 4518631e9bdSSteffen Klassert { 4528631e9bdSSteffen Klassert u8 *auth_data; 4538631e9bdSSteffen Klassert u8 *icv; 4548631e9bdSSteffen Klassert u8 *work_iph; 4558631e9bdSSteffen Klassert struct sk_buff *skb = base->data; 4568631e9bdSSteffen Klassert struct xfrm_state *x = xfrm_input_state(skb); 4578631e9bdSSteffen Klassert struct ah_data *ahp = x->data; 4588631e9bdSSteffen Klassert struct ip_auth_hdr *ah = ip_auth_hdr(skb); 4598631e9bdSSteffen Klassert int hdr_len = skb_network_header_len(skb); 4608631e9bdSSteffen Klassert int ah_hlen = (ah->hdrlen + 2) << 2; 4618631e9bdSSteffen Klassert 4628631e9bdSSteffen Klassert work_iph = AH_SKB_CB(skb)->tmp; 4638631e9bdSSteffen Klassert auth_data = ah_tmp_auth(work_iph, hdr_len); 4648631e9bdSSteffen Klassert icv = ah_tmp_icv(ahp->ahash, auth_data, ahp->icv_trunc_len); 4658631e9bdSSteffen Klassert 4668631e9bdSSteffen Klassert err = memcmp(icv, auth_data, ahp->icv_trunc_len) ? -EBADMSG: 0; 4678631e9bdSSteffen Klassert if (err) 4688631e9bdSSteffen Klassert goto out; 4698631e9bdSSteffen Klassert 470b7ea81a5SNick Bowler err = ah->nexthdr; 471b7ea81a5SNick Bowler 4728631e9bdSSteffen Klassert skb->network_header += ah_hlen; 4738631e9bdSSteffen Klassert memcpy(skb_network_header(skb), work_iph, hdr_len); 4748631e9bdSSteffen Klassert __skb_pull(skb, ah_hlen + hdr_len); 4758631e9bdSSteffen Klassert skb_set_transport_header(skb, -hdr_len); 4768631e9bdSSteffen Klassert out: 4778631e9bdSSteffen Klassert kfree(AH_SKB_CB(skb)->tmp); 4788631e9bdSSteffen Klassert xfrm_input_resume(skb, err); 4798631e9bdSSteffen Klassert } 4808631e9bdSSteffen Klassert 4818631e9bdSSteffen Klassert 4828631e9bdSSteffen Klassert 483e695633eSHerbert Xu static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) 4841da177e4SLinus Torvalds { 4851da177e4SLinus Torvalds /* 4861da177e4SLinus Torvalds * Before process AH 4871da177e4SLinus Torvalds * [IPv6][Ext1][Ext2][AH][Dest][Payload] 4881da177e4SLinus Torvalds * |<-------------->| hdr_len 4891da177e4SLinus Torvalds * 4901da177e4SLinus Torvalds * To erase AH: 4911da177e4SLinus Torvalds * Keeping copy of cleared headers. After AH processing, 492b0e380b1SArnaldo Carvalho de Melo * Moving the pointer of skb->network_header by using skb_pull as long 493b0e380b1SArnaldo Carvalho de Melo * as AH header length. Then copy back the copy as long as hdr_len 4941da177e4SLinus Torvalds * If destination header following AH exists, copy it into after [Ext2]. 4951da177e4SLinus Torvalds * 4961da177e4SLinus Torvalds * |<>|[IPv6][Ext1][Ext2][Dest][Payload] 4971da177e4SLinus Torvalds * There is offset of AH before IPv6 header after the process. 4981da177e4SLinus Torvalds */ 4991da177e4SLinus Torvalds 5008631e9bdSSteffen Klassert u8 *auth_data; 5018631e9bdSSteffen Klassert u8 *icv; 5028631e9bdSSteffen Klassert u8 *work_iph; 5038631e9bdSSteffen Klassert struct sk_buff *trailer; 5048631e9bdSSteffen Klassert struct crypto_ahash *ahash; 5058631e9bdSSteffen Klassert struct ahash_request *req; 5068631e9bdSSteffen Klassert struct scatterlist *sg; 50787bdc48dSHerbert Xu struct ip_auth_hdr *ah; 5080660e03fSArnaldo Carvalho de Melo struct ipv6hdr *ip6h; 5091da177e4SLinus Torvalds struct ah_data *ahp; 5101da177e4SLinus Torvalds u16 hdr_len; 5111da177e4SLinus Torvalds u16 ah_hlen; 5121da177e4SLinus Torvalds int nexthdr; 5138631e9bdSSteffen Klassert int nfrags; 5148631e9bdSSteffen Klassert int err = -ENOMEM; 5151da177e4SLinus Torvalds 5161da177e4SLinus Torvalds if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr))) 5171da177e4SLinus Torvalds goto out; 5181da177e4SLinus Torvalds 5191da177e4SLinus Torvalds /* We are going to _remove_ AH header to keep sockets happy, 5201da177e4SLinus Torvalds * so... Later this can change. */ 5211da177e4SLinus Torvalds if (skb_cloned(skb) && 5221da177e4SLinus Torvalds pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) 5231da177e4SLinus Torvalds goto out; 5241da177e4SLinus Torvalds 5257aa68cb9SHerbert Xu skb->ip_summed = CHECKSUM_NONE; 5267aa68cb9SHerbert Xu 5278631e9bdSSteffen Klassert hdr_len = skb_network_header_len(skb); 52887bdc48dSHerbert Xu ah = (struct ip_auth_hdr *)skb->data; 5291da177e4SLinus Torvalds ahp = x->data; 5308631e9bdSSteffen Klassert ahash = ahp->ahash; 5318631e9bdSSteffen Klassert 5321da177e4SLinus Torvalds nexthdr = ah->nexthdr; 5331da177e4SLinus Torvalds ah_hlen = (ah->hdrlen + 2) << 2; 5341da177e4SLinus Torvalds 53587bdc48dSHerbert Xu if (ah_hlen != XFRM_ALIGN8(sizeof(*ah) + ahp->icv_full_len) && 53687bdc48dSHerbert Xu ah_hlen != XFRM_ALIGN8(sizeof(*ah) + ahp->icv_trunc_len)) 5371da177e4SLinus Torvalds goto out; 5381da177e4SLinus Torvalds 5391da177e4SLinus Torvalds if (!pskb_may_pull(skb, ah_hlen)) 5401da177e4SLinus Torvalds goto out; 5411da177e4SLinus Torvalds 5428631e9bdSSteffen Klassert 5438631e9bdSSteffen Klassert if ((err = skb_cow_data(skb, 0, &trailer)) < 0) 5448631e9bdSSteffen Klassert goto out; 5458631e9bdSSteffen Klassert nfrags = err; 5468631e9bdSSteffen Klassert 5474b0ef1f2SDang Hongwu ah = (struct ip_auth_hdr *)skb->data; 5484b0ef1f2SDang Hongwu ip6h = ipv6_hdr(skb); 5494b0ef1f2SDang Hongwu 5504b0ef1f2SDang Hongwu skb_push(skb, hdr_len); 5514b0ef1f2SDang Hongwu 5528631e9bdSSteffen Klassert work_iph = ah_alloc_tmp(ahash, nfrags, hdr_len + ahp->icv_trunc_len); 5538631e9bdSSteffen Klassert if (!work_iph) 5548631e9bdSSteffen Klassert goto out; 5558631e9bdSSteffen Klassert 5568631e9bdSSteffen Klassert auth_data = ah_tmp_auth(work_iph, hdr_len); 5578631e9bdSSteffen Klassert icv = ah_tmp_icv(ahash, auth_data, ahp->icv_trunc_len); 5588631e9bdSSteffen Klassert req = ah_tmp_req(ahash, icv); 5598631e9bdSSteffen Klassert sg = ah_req_sg(ahash, req); 5608631e9bdSSteffen Klassert 5618631e9bdSSteffen Klassert memcpy(work_iph, ip6h, hdr_len); 5628631e9bdSSteffen Klassert memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len); 5638631e9bdSSteffen Klassert memset(ah->auth_data, 0, ahp->icv_trunc_len); 5648631e9bdSSteffen Klassert 5650660e03fSArnaldo Carvalho de Melo if (ipv6_clear_mutable_options(ip6h, hdr_len, XFRM_POLICY_IN)) 5668631e9bdSSteffen Klassert goto out_free; 5678631e9bdSSteffen Klassert 5680660e03fSArnaldo Carvalho de Melo ip6h->priority = 0; 5690660e03fSArnaldo Carvalho de Melo ip6h->flow_lbl[0] = 0; 5700660e03fSArnaldo Carvalho de Melo ip6h->flow_lbl[1] = 0; 5710660e03fSArnaldo Carvalho de Melo ip6h->flow_lbl[2] = 0; 5720660e03fSArnaldo Carvalho de Melo ip6h->hop_limit = 0; 5731da177e4SLinus Torvalds 5748631e9bdSSteffen Klassert sg_init_table(sg, nfrags); 5758631e9bdSSteffen Klassert skb_to_sgvec(skb, sg, 0, skb->len); 5761da177e4SLinus Torvalds 5778631e9bdSSteffen Klassert ahash_request_set_crypt(req, sg, icv, skb->len); 5788631e9bdSSteffen Klassert ahash_request_set_callback(req, 0, ah6_input_done, skb); 5798631e9bdSSteffen Klassert 5808631e9bdSSteffen Klassert AH_SKB_CB(skb)->tmp = work_iph; 5818631e9bdSSteffen Klassert 5828631e9bdSSteffen Klassert err = crypto_ahash_digest(req); 5838631e9bdSSteffen Klassert if (err) { 5848631e9bdSSteffen Klassert if (err == -EINPROGRESS) 5858631e9bdSSteffen Klassert goto out; 5868631e9bdSSteffen Klassert 5878631e9bdSSteffen Klassert goto out_free; 5880ebea8efSHerbert Xu } 5890ebea8efSHerbert Xu 5908631e9bdSSteffen Klassert err = memcmp(icv, auth_data, ahp->icv_trunc_len) ? -EBADMSG: 0; 5910ebea8efSHerbert Xu if (err) 5928631e9bdSSteffen Klassert goto out_free; 5931da177e4SLinus Torvalds 594b0e380b1SArnaldo Carvalho de Melo skb->network_header += ah_hlen; 5958631e9bdSSteffen Klassert memcpy(skb_network_header(skb), work_iph, hdr_len); 596b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header; 59731a4ab93SHerbert Xu __skb_pull(skb, ah_hlen + hdr_len); 5981da177e4SLinus Torvalds 5998631e9bdSSteffen Klassert err = nexthdr; 6001da177e4SLinus Torvalds 6018631e9bdSSteffen Klassert out_free: 6028631e9bdSSteffen Klassert kfree(work_iph); 6031da177e4SLinus Torvalds out: 60407d4ee58SHerbert Xu return err; 6051da177e4SLinus Torvalds } 6061da177e4SLinus Torvalds 6071da177e4SLinus Torvalds static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 608d5fdd6baSBrian Haley u8 type, u8 code, int offset, __be32 info) 6091da177e4SLinus Torvalds { 6104fb236baSAlexey Dobriyan struct net *net = dev_net(skb->dev); 6111da177e4SLinus Torvalds struct ipv6hdr *iph = (struct ipv6hdr*)skb->data; 6121da177e4SLinus Torvalds struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+offset); 6131da177e4SLinus Torvalds struct xfrm_state *x; 6141da177e4SLinus Torvalds 6151da177e4SLinus Torvalds if (type != ICMPV6_DEST_UNREACH && 616ec18d9a2SDavid S. Miller type != ICMPV6_PKT_TOOBIG && 617ec18d9a2SDavid S. Miller type != NDISC_REDIRECT) 6181da177e4SLinus Torvalds return; 6191da177e4SLinus Torvalds 620bd55775cSJamal Hadi Salim x = xfrm_state_lookup(net, skb->mark, (xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET6); 6211da177e4SLinus Torvalds if (!x) 6221da177e4SLinus Torvalds return; 6231da177e4SLinus Torvalds 624ec18d9a2SDavid S. Miller if (type == NDISC_REDIRECT) 625ec18d9a2SDavid S. Miller ip6_redirect(skb, net, 0, 0); 626ec18d9a2SDavid S. Miller else 62781aded24SDavid S. Miller ip6_update_pmtu(skb, net, info, 0, 0); 6281da177e4SLinus Torvalds xfrm_state_put(x); 6291da177e4SLinus Torvalds } 6301da177e4SLinus Torvalds 63172cb6962SHerbert Xu static int ah6_init_state(struct xfrm_state *x) 6321da177e4SLinus Torvalds { 6331da177e4SLinus Torvalds struct ah_data *ahp = NULL; 6341da177e4SLinus Torvalds struct xfrm_algo_desc *aalg_desc; 6358631e9bdSSteffen Klassert struct crypto_ahash *ahash; 6361da177e4SLinus Torvalds 6371da177e4SLinus Torvalds if (!x->aalg) 6381da177e4SLinus Torvalds goto error; 6391da177e4SLinus Torvalds 6401da177e4SLinus Torvalds if (x->encap) 6411da177e4SLinus Torvalds goto error; 6421da177e4SLinus Torvalds 6430c600edaSIngo Oeser ahp = kzalloc(sizeof(*ahp), GFP_KERNEL); 6441da177e4SLinus Torvalds if (ahp == NULL) 6451da177e4SLinus Torvalds return -ENOMEM; 6461da177e4SLinus Torvalds 6478631e9bdSSteffen Klassert ahash = crypto_alloc_ahash(x->aalg->alg_name, 0, 0); 6488631e9bdSSteffen Klassert if (IS_ERR(ahash)) 6491da177e4SLinus Torvalds goto error; 65007d4ee58SHerbert Xu 6518631e9bdSSteffen Klassert ahp->ahash = ahash; 6528631e9bdSSteffen Klassert if (crypto_ahash_setkey(ahash, x->aalg->alg_key, 653bc31d3b2SHerbert Xu (x->aalg->alg_key_len + 7) / 8)) 65407d4ee58SHerbert Xu goto error; 6551da177e4SLinus Torvalds 6561da177e4SLinus Torvalds /* 6571da177e4SLinus Torvalds * Lookup the algorithm description maintained by xfrm_algo, 6581da177e4SLinus Torvalds * verify crypto transform properties, and store information 6591da177e4SLinus Torvalds * we need for AH processing. This lookup cannot fail here 66007d4ee58SHerbert Xu * after a successful crypto_alloc_hash(). 6611da177e4SLinus Torvalds */ 6621da177e4SLinus Torvalds aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0); 6631da177e4SLinus Torvalds BUG_ON(!aalg_desc); 6641da177e4SLinus Torvalds 6651da177e4SLinus Torvalds if (aalg_desc->uinfo.auth.icv_fullbits/8 != 6668631e9bdSSteffen Klassert crypto_ahash_digestsize(ahash)) { 667f3213831SJoe Perches pr_info("AH: %s digestsize %u != %hu\n", 6688631e9bdSSteffen Klassert x->aalg->alg_name, crypto_ahash_digestsize(ahash), 6691da177e4SLinus Torvalds aalg_desc->uinfo.auth.icv_fullbits/8); 6701da177e4SLinus Torvalds goto error; 6711da177e4SLinus Torvalds } 6721da177e4SLinus Torvalds 6731da177e4SLinus Torvalds ahp->icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8; 6748f8a088cSMartin Willi ahp->icv_trunc_len = x->aalg->alg_trunc_len/8; 6751da177e4SLinus Torvalds 6761da177e4SLinus Torvalds BUG_ON(ahp->icv_trunc_len > MAX_AH_AUTH_LEN); 6771da177e4SLinus Torvalds 67887bdc48dSHerbert Xu x->props.header_len = XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + 67987bdc48dSHerbert Xu ahp->icv_trunc_len); 680ca68145fSHerbert Xu switch (x->props.mode) { 681ca68145fSHerbert Xu case XFRM_MODE_BEET: 682ca68145fSHerbert Xu case XFRM_MODE_TRANSPORT: 683ca68145fSHerbert Xu break; 684ca68145fSHerbert Xu case XFRM_MODE_TUNNEL: 6851da177e4SLinus Torvalds x->props.header_len += sizeof(struct ipv6hdr); 686ea2c47b4SMasahide NAKAMURA break; 687ca68145fSHerbert Xu default: 688ca68145fSHerbert Xu goto error; 689ca68145fSHerbert Xu } 6901da177e4SLinus Torvalds x->data = ahp; 6911da177e4SLinus Torvalds 6921da177e4SLinus Torvalds return 0; 6931da177e4SLinus Torvalds 6941da177e4SLinus Torvalds error: 6951da177e4SLinus Torvalds if (ahp) { 6968631e9bdSSteffen Klassert crypto_free_ahash(ahp->ahash); 6971da177e4SLinus Torvalds kfree(ahp); 6981da177e4SLinus Torvalds } 6991da177e4SLinus Torvalds return -EINVAL; 7001da177e4SLinus Torvalds } 7011da177e4SLinus Torvalds 7021da177e4SLinus Torvalds static void ah6_destroy(struct xfrm_state *x) 7031da177e4SLinus Torvalds { 7041da177e4SLinus Torvalds struct ah_data *ahp = x->data; 7051da177e4SLinus Torvalds 7061da177e4SLinus Torvalds if (!ahp) 7071da177e4SLinus Torvalds return; 7081da177e4SLinus Torvalds 7098631e9bdSSteffen Klassert crypto_free_ahash(ahp->ahash); 7101da177e4SLinus Torvalds kfree(ahp); 7111da177e4SLinus Torvalds } 7121da177e4SLinus Torvalds 713533cb5b0SEric Dumazet static const struct xfrm_type ah6_type = 7141da177e4SLinus Torvalds { 7151da177e4SLinus Torvalds .description = "AH6", 7161da177e4SLinus Torvalds .owner = THIS_MODULE, 7171da177e4SLinus Torvalds .proto = IPPROTO_AH, 718436a0a40SHerbert Xu .flags = XFRM_TYPE_REPLAY_PROT, 7191da177e4SLinus Torvalds .init_state = ah6_init_state, 7201da177e4SLinus Torvalds .destructor = ah6_destroy, 7211da177e4SLinus Torvalds .input = ah6_input, 722aee5adb4SMasahide NAKAMURA .output = ah6_output, 723aee5adb4SMasahide NAKAMURA .hdr_offset = xfrm6_find_1stfragopt, 7241da177e4SLinus Torvalds }; 7251da177e4SLinus Torvalds 72641135cc8SAlexey Dobriyan static const struct inet6_protocol ah6_protocol = { 7271da177e4SLinus Torvalds .handler = xfrm6_rcv, 7281da177e4SLinus Torvalds .err_handler = ah6_err, 7291da177e4SLinus Torvalds .flags = INET6_PROTO_NOPOLICY, 7301da177e4SLinus Torvalds }; 7311da177e4SLinus Torvalds 7321da177e4SLinus Torvalds static int __init ah6_init(void) 7331da177e4SLinus Torvalds { 7341da177e4SLinus Torvalds if (xfrm_register_type(&ah6_type, AF_INET6) < 0) { 735f3213831SJoe Perches pr_info("%s: can't add xfrm type\n", __func__); 7361da177e4SLinus Torvalds return -EAGAIN; 7371da177e4SLinus Torvalds } 7381da177e4SLinus Torvalds 7391da177e4SLinus Torvalds if (inet6_add_protocol(&ah6_protocol, IPPROTO_AH) < 0) { 740f3213831SJoe Perches pr_info("%s: can't add protocol\n", __func__); 7411da177e4SLinus Torvalds xfrm_unregister_type(&ah6_type, AF_INET6); 7421da177e4SLinus Torvalds return -EAGAIN; 7431da177e4SLinus Torvalds } 7441da177e4SLinus Torvalds 7451da177e4SLinus Torvalds return 0; 7461da177e4SLinus Torvalds } 7471da177e4SLinus Torvalds 7481da177e4SLinus Torvalds static void __exit ah6_fini(void) 7491da177e4SLinus Torvalds { 7501da177e4SLinus Torvalds if (inet6_del_protocol(&ah6_protocol, IPPROTO_AH) < 0) 751f3213831SJoe Perches pr_info("%s: can't remove protocol\n", __func__); 7521da177e4SLinus Torvalds 7531da177e4SLinus Torvalds if (xfrm_unregister_type(&ah6_type, AF_INET6) < 0) 754f3213831SJoe Perches pr_info("%s: can't remove xfrm type\n", __func__); 7551da177e4SLinus Torvalds 7561da177e4SLinus Torvalds } 7571da177e4SLinus Torvalds 7581da177e4SLinus Torvalds module_init(ah6_init); 7591da177e4SLinus Torvalds module_exit(ah6_fini); 7601da177e4SLinus Torvalds 7611da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 762d3d6dd3aSMasahide NAKAMURA MODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_AH); 763