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> 381da177e4SLinus Torvalds #include <net/icmp.h> 391da177e4SLinus Torvalds #include <net/ipv6.h> 4014c85021SArnaldo Carvalho de Melo #include <net/protocol.h> 411da177e4SLinus Torvalds #include <net/xfrm.h> 421da177e4SLinus Torvalds 438631e9bdSSteffen Klassert #define IPV6HDR_BASELEN 8 448631e9bdSSteffen Klassert 458631e9bdSSteffen Klassert struct tmp_ext { 468631e9bdSSteffen Klassert #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 478631e9bdSSteffen Klassert struct in6_addr saddr; 488631e9bdSSteffen Klassert #endif 498631e9bdSSteffen Klassert struct in6_addr daddr; 508631e9bdSSteffen Klassert char hdrs[0]; 518631e9bdSSteffen Klassert }; 528631e9bdSSteffen Klassert 538631e9bdSSteffen Klassert struct ah_skb_cb { 548631e9bdSSteffen Klassert struct xfrm_skb_cb xfrm; 558631e9bdSSteffen Klassert void *tmp; 568631e9bdSSteffen Klassert }; 578631e9bdSSteffen Klassert 588631e9bdSSteffen Klassert #define AH_SKB_CB(__skb) ((struct ah_skb_cb *)&((__skb)->cb[0])) 598631e9bdSSteffen Klassert 608631e9bdSSteffen Klassert static void *ah_alloc_tmp(struct crypto_ahash *ahash, int nfrags, 618631e9bdSSteffen Klassert unsigned int size) 628631e9bdSSteffen Klassert { 638631e9bdSSteffen Klassert unsigned int len; 648631e9bdSSteffen Klassert 658631e9bdSSteffen Klassert len = size + crypto_ahash_digestsize(ahash) + 668631e9bdSSteffen Klassert (crypto_ahash_alignmask(ahash) & 678631e9bdSSteffen Klassert ~(crypto_tfm_ctx_alignment() - 1)); 688631e9bdSSteffen Klassert 698631e9bdSSteffen Klassert len = ALIGN(len, crypto_tfm_ctx_alignment()); 708631e9bdSSteffen Klassert 718631e9bdSSteffen Klassert len += sizeof(struct ahash_request) + crypto_ahash_reqsize(ahash); 728631e9bdSSteffen Klassert len = ALIGN(len, __alignof__(struct scatterlist)); 738631e9bdSSteffen Klassert 748631e9bdSSteffen Klassert len += sizeof(struct scatterlist) * nfrags; 758631e9bdSSteffen Klassert 768631e9bdSSteffen Klassert return kmalloc(len, GFP_ATOMIC); 778631e9bdSSteffen Klassert } 788631e9bdSSteffen Klassert 798631e9bdSSteffen Klassert static inline struct tmp_ext *ah_tmp_ext(void *base) 808631e9bdSSteffen Klassert { 818631e9bdSSteffen Klassert return base + IPV6HDR_BASELEN; 828631e9bdSSteffen Klassert } 838631e9bdSSteffen Klassert 848631e9bdSSteffen Klassert static inline u8 *ah_tmp_auth(u8 *tmp, unsigned int offset) 858631e9bdSSteffen Klassert { 868631e9bdSSteffen Klassert return tmp + offset; 878631e9bdSSteffen Klassert } 888631e9bdSSteffen Klassert 898631e9bdSSteffen Klassert static inline u8 *ah_tmp_icv(struct crypto_ahash *ahash, void *tmp, 908631e9bdSSteffen Klassert unsigned int offset) 918631e9bdSSteffen Klassert { 928631e9bdSSteffen Klassert return PTR_ALIGN((u8 *)tmp + offset, crypto_ahash_alignmask(ahash) + 1); 938631e9bdSSteffen Klassert } 948631e9bdSSteffen Klassert 958631e9bdSSteffen Klassert static inline struct ahash_request *ah_tmp_req(struct crypto_ahash *ahash, 968631e9bdSSteffen Klassert u8 *icv) 978631e9bdSSteffen Klassert { 988631e9bdSSteffen Klassert struct ahash_request *req; 998631e9bdSSteffen Klassert 1008631e9bdSSteffen Klassert req = (void *)PTR_ALIGN(icv + crypto_ahash_digestsize(ahash), 1018631e9bdSSteffen Klassert crypto_tfm_ctx_alignment()); 1028631e9bdSSteffen Klassert 1038631e9bdSSteffen Klassert ahash_request_set_tfm(req, ahash); 1048631e9bdSSteffen Klassert 1058631e9bdSSteffen Klassert return req; 1068631e9bdSSteffen Klassert } 1078631e9bdSSteffen Klassert 1088631e9bdSSteffen Klassert static inline struct scatterlist *ah_req_sg(struct crypto_ahash *ahash, 1098631e9bdSSteffen Klassert struct ahash_request *req) 1108631e9bdSSteffen Klassert { 1118631e9bdSSteffen Klassert return (void *)ALIGN((unsigned long)(req + 1) + 1128631e9bdSSteffen Klassert crypto_ahash_reqsize(ahash), 1138631e9bdSSteffen Klassert __alignof__(struct scatterlist)); 1148631e9bdSSteffen Klassert } 1158631e9bdSSteffen Klassert 1161da177e4SLinus Torvalds static int zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr) 1171da177e4SLinus Torvalds { 1181da177e4SLinus Torvalds u8 *opt = (u8 *)opthdr; 1191da177e4SLinus Torvalds int len = ipv6_optlen(opthdr); 1201da177e4SLinus Torvalds int off = 0; 1211da177e4SLinus Torvalds int optlen = 0; 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds off += 2; 1241da177e4SLinus Torvalds len -= 2; 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds while (len > 0) { 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds switch (opt[off]) { 1291da177e4SLinus Torvalds 1301de5a71cSEldad Zack case IPV6_TLV_PAD1: 1311da177e4SLinus Torvalds optlen = 1; 1321da177e4SLinus Torvalds break; 1331da177e4SLinus Torvalds default: 1341da177e4SLinus Torvalds if (len < 2) 1351da177e4SLinus Torvalds goto bad; 1361da177e4SLinus Torvalds optlen = opt[off+1]+2; 1371da177e4SLinus Torvalds if (len < optlen) 1381da177e4SLinus Torvalds goto bad; 1391da177e4SLinus Torvalds if (opt[off] & 0x20) 1401da177e4SLinus Torvalds memset(&opt[off+2], 0, opt[off+1]); 1411da177e4SLinus Torvalds break; 1421da177e4SLinus Torvalds } 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds off += optlen; 1451da177e4SLinus Torvalds len -= optlen; 1461da177e4SLinus Torvalds } 1471da177e4SLinus Torvalds if (len == 0) 1481da177e4SLinus Torvalds return 1; 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds bad: 1511da177e4SLinus Torvalds return 0; 1521da177e4SLinus Torvalds } 1531da177e4SLinus Torvalds 15459fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 15527637df9SMasahide NAKAMURA /** 15627637df9SMasahide NAKAMURA * ipv6_rearrange_destopt - rearrange IPv6 destination options header 15727637df9SMasahide NAKAMURA * @iph: IPv6 header 15827637df9SMasahide NAKAMURA * @destopt: destionation options header 15927637df9SMasahide NAKAMURA */ 16027637df9SMasahide NAKAMURA static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *destopt) 16127637df9SMasahide NAKAMURA { 16227637df9SMasahide NAKAMURA u8 *opt = (u8 *)destopt; 16327637df9SMasahide NAKAMURA int len = ipv6_optlen(destopt); 16427637df9SMasahide NAKAMURA int off = 0; 16527637df9SMasahide NAKAMURA int optlen = 0; 16627637df9SMasahide NAKAMURA 16727637df9SMasahide NAKAMURA off += 2; 16827637df9SMasahide NAKAMURA len -= 2; 16927637df9SMasahide NAKAMURA 17027637df9SMasahide NAKAMURA while (len > 0) { 17127637df9SMasahide NAKAMURA 17227637df9SMasahide NAKAMURA switch (opt[off]) { 17327637df9SMasahide NAKAMURA 1741de5a71cSEldad Zack case IPV6_TLV_PAD1: 17527637df9SMasahide NAKAMURA optlen = 1; 17627637df9SMasahide NAKAMURA break; 17727637df9SMasahide NAKAMURA default: 17827637df9SMasahide NAKAMURA if (len < 2) 17927637df9SMasahide NAKAMURA goto bad; 18027637df9SMasahide NAKAMURA optlen = opt[off+1]+2; 18127637df9SMasahide NAKAMURA if (len < optlen) 18227637df9SMasahide NAKAMURA goto bad; 18327637df9SMasahide NAKAMURA 18427637df9SMasahide NAKAMURA /* Rearrange the source address in @iph and the 18527637df9SMasahide NAKAMURA * addresses in home address option for final source. 18627637df9SMasahide NAKAMURA * See 11.3.2 of RFC 3775 for details. 18727637df9SMasahide NAKAMURA */ 18827637df9SMasahide NAKAMURA if (opt[off] == IPV6_TLV_HAO) { 18927637df9SMasahide NAKAMURA struct in6_addr final_addr; 19027637df9SMasahide NAKAMURA struct ipv6_destopt_hao *hao; 19127637df9SMasahide NAKAMURA 19227637df9SMasahide NAKAMURA hao = (struct ipv6_destopt_hao *)&opt[off]; 19327637df9SMasahide NAKAMURA if (hao->length != sizeof(hao->addr)) { 194e87cc472SJoe Perches net_warn_ratelimited("destopt hao: invalid header length: %u\n", 195e87cc472SJoe Perches hao->length); 19627637df9SMasahide NAKAMURA goto bad; 19727637df9SMasahide NAKAMURA } 1984e3fd7a0SAlexey Dobriyan final_addr = hao->addr; 1994e3fd7a0SAlexey Dobriyan hao->addr = iph->saddr; 2004e3fd7a0SAlexey Dobriyan iph->saddr = final_addr; 20127637df9SMasahide NAKAMURA } 20227637df9SMasahide NAKAMURA break; 20327637df9SMasahide NAKAMURA } 20427637df9SMasahide NAKAMURA 20527637df9SMasahide NAKAMURA off += optlen; 20627637df9SMasahide NAKAMURA len -= optlen; 20727637df9SMasahide NAKAMURA } 208e731c248SYOSHIFUJI Hideaki /* Note: ok if len == 0 */ 20927637df9SMasahide NAKAMURA bad: 21027637df9SMasahide NAKAMURA return; 21127637df9SMasahide NAKAMURA } 212136ebf08SMasahide NAKAMURA #else 213136ebf08SMasahide NAKAMURA static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *destopt) {} 21427637df9SMasahide NAKAMURA #endif 21527637df9SMasahide NAKAMURA 2161da177e4SLinus Torvalds /** 2171da177e4SLinus Torvalds * ipv6_rearrange_rthdr - rearrange IPv6 routing header 2181da177e4SLinus Torvalds * @iph: IPv6 header 2191da177e4SLinus Torvalds * @rthdr: routing header 2201da177e4SLinus Torvalds * 2211da177e4SLinus Torvalds * Rearrange the destination address in @iph and the addresses in @rthdr 2221da177e4SLinus Torvalds * so that they appear in the order they will at the final destination. 2231da177e4SLinus Torvalds * See Appendix A2 of RFC 2402 for details. 2241da177e4SLinus Torvalds */ 2251da177e4SLinus Torvalds static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr) 2261da177e4SLinus Torvalds { 2271da177e4SLinus Torvalds int segments, segments_left; 2281da177e4SLinus Torvalds struct in6_addr *addrs; 2291da177e4SLinus Torvalds struct in6_addr final_addr; 2301da177e4SLinus Torvalds 2311da177e4SLinus Torvalds segments_left = rthdr->segments_left; 2321da177e4SLinus Torvalds if (segments_left == 0) 2331da177e4SLinus Torvalds return; 2341da177e4SLinus Torvalds rthdr->segments_left = 0; 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds /* The value of rthdr->hdrlen has been verified either by the system 2371da177e4SLinus Torvalds * call if it is locally generated, or by ipv6_rthdr_rcv() for incoming 2381da177e4SLinus Torvalds * packets. So we can assume that it is even and that segments is 2391da177e4SLinus Torvalds * greater than or equal to segments_left. 2401da177e4SLinus Torvalds * 2411da177e4SLinus Torvalds * For the same reason we can assume that this option is of type 0. 2421da177e4SLinus Torvalds */ 2431da177e4SLinus Torvalds segments = rthdr->hdrlen >> 1; 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds addrs = ((struct rt0_hdr *)rthdr)->addr; 2464e3fd7a0SAlexey Dobriyan final_addr = addrs[segments - 1]; 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds addrs += segments - segments_left; 2491da177e4SLinus Torvalds memmove(addrs + 1, addrs, (segments_left - 1) * sizeof(*addrs)); 2501da177e4SLinus Torvalds 2514e3fd7a0SAlexey Dobriyan addrs[0] = iph->daddr; 2524e3fd7a0SAlexey Dobriyan iph->daddr = final_addr; 2531da177e4SLinus Torvalds } 2541da177e4SLinus Torvalds 25527637df9SMasahide NAKAMURA static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir) 2561da177e4SLinus Torvalds { 2571da177e4SLinus Torvalds union { 2581da177e4SLinus Torvalds struct ipv6hdr *iph; 2591da177e4SLinus Torvalds struct ipv6_opt_hdr *opth; 2601da177e4SLinus Torvalds struct ipv6_rt_hdr *rth; 2611da177e4SLinus Torvalds char *raw; 2621da177e4SLinus Torvalds } exthdr = { .iph = iph }; 2631da177e4SLinus Torvalds char *end = exthdr.raw + len; 2641da177e4SLinus Torvalds int nexthdr = iph->nexthdr; 2651da177e4SLinus Torvalds 2661da177e4SLinus Torvalds exthdr.iph++; 2671da177e4SLinus Torvalds 2681da177e4SLinus Torvalds while (exthdr.raw < end) { 2691da177e4SLinus Torvalds switch (nexthdr) { 27027637df9SMasahide NAKAMURA case NEXTHDR_DEST: 27127637df9SMasahide NAKAMURA if (dir == XFRM_POLICY_OUT) 27227637df9SMasahide NAKAMURA ipv6_rearrange_destopt(iph, exthdr.opth); 273e731c248SYOSHIFUJI Hideaki case NEXTHDR_HOP: 274e731c248SYOSHIFUJI Hideaki if (!zero_out_mutable_opts(exthdr.opth)) { 275e731c248SYOSHIFUJI Hideaki LIMIT_NETDEBUG( 276e731c248SYOSHIFUJI Hideaki KERN_WARNING "overrun %sopts\n", 277e731c248SYOSHIFUJI Hideaki nexthdr == NEXTHDR_HOP ? 278e731c248SYOSHIFUJI Hideaki "hop" : "dest"); 279e731c248SYOSHIFUJI Hideaki return -EINVAL; 280e731c248SYOSHIFUJI Hideaki } 281e731c248SYOSHIFUJI Hideaki break; 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds case NEXTHDR_ROUTING: 2841da177e4SLinus Torvalds ipv6_rearrange_rthdr(iph, exthdr.rth); 2851da177e4SLinus Torvalds break; 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds default : 2881da177e4SLinus Torvalds return 0; 2891da177e4SLinus Torvalds } 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds nexthdr = exthdr.opth->nexthdr; 2921da177e4SLinus Torvalds exthdr.raw += ipv6_optlen(exthdr.opth); 2931da177e4SLinus Torvalds } 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds return 0; 2961da177e4SLinus Torvalds } 2971da177e4SLinus Torvalds 2988631e9bdSSteffen Klassert static void ah6_output_done(struct crypto_async_request *base, int err) 2998631e9bdSSteffen Klassert { 3008631e9bdSSteffen Klassert int extlen; 3018631e9bdSSteffen Klassert u8 *iph_base; 3028631e9bdSSteffen Klassert u8 *icv; 3038631e9bdSSteffen Klassert struct sk_buff *skb = base->data; 3048631e9bdSSteffen Klassert struct xfrm_state *x = skb_dst(skb)->xfrm; 3058631e9bdSSteffen Klassert struct ah_data *ahp = x->data; 3068631e9bdSSteffen Klassert struct ipv6hdr *top_iph = ipv6_hdr(skb); 3078631e9bdSSteffen Klassert struct ip_auth_hdr *ah = ip_auth_hdr(skb); 3088631e9bdSSteffen Klassert struct tmp_ext *iph_ext; 3098631e9bdSSteffen Klassert 3108631e9bdSSteffen Klassert extlen = skb_network_header_len(skb) - sizeof(struct ipv6hdr); 3118631e9bdSSteffen Klassert if (extlen) 3128631e9bdSSteffen Klassert extlen += sizeof(*iph_ext); 3138631e9bdSSteffen Klassert 3148631e9bdSSteffen Klassert iph_base = AH_SKB_CB(skb)->tmp; 3158631e9bdSSteffen Klassert iph_ext = ah_tmp_ext(iph_base); 3168631e9bdSSteffen Klassert icv = ah_tmp_icv(ahp->ahash, iph_ext, extlen); 3178631e9bdSSteffen Klassert 3188631e9bdSSteffen Klassert memcpy(ah->auth_data, icv, ahp->icv_trunc_len); 3198631e9bdSSteffen Klassert memcpy(top_iph, iph_base, IPV6HDR_BASELEN); 3208631e9bdSSteffen Klassert 3218631e9bdSSteffen Klassert if (extlen) { 3228631e9bdSSteffen Klassert #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 3238631e9bdSSteffen Klassert memcpy(&top_iph->saddr, iph_ext, extlen); 3248631e9bdSSteffen Klassert #else 3258631e9bdSSteffen Klassert memcpy(&top_iph->daddr, iph_ext, extlen); 3268631e9bdSSteffen Klassert #endif 3278631e9bdSSteffen Klassert } 3288631e9bdSSteffen Klassert 3298631e9bdSSteffen Klassert kfree(AH_SKB_CB(skb)->tmp); 3308631e9bdSSteffen Klassert xfrm_output_resume(skb, err); 3318631e9bdSSteffen Klassert } 3328631e9bdSSteffen Klassert 3331da177e4SLinus Torvalds static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) 3341da177e4SLinus Torvalds { 3351da177e4SLinus Torvalds int err; 3368631e9bdSSteffen Klassert int nfrags; 3371da177e4SLinus Torvalds int extlen; 3388631e9bdSSteffen Klassert u8 *iph_base; 3398631e9bdSSteffen Klassert u8 *icv; 3408631e9bdSSteffen Klassert u8 nexthdr; 3418631e9bdSSteffen Klassert struct sk_buff *trailer; 3428631e9bdSSteffen Klassert struct crypto_ahash *ahash; 3438631e9bdSSteffen Klassert struct ahash_request *req; 3448631e9bdSSteffen Klassert struct scatterlist *sg; 3451da177e4SLinus Torvalds struct ipv6hdr *top_iph; 3461da177e4SLinus Torvalds struct ip_auth_hdr *ah; 3471da177e4SLinus Torvalds struct ah_data *ahp; 3488631e9bdSSteffen Klassert struct tmp_ext *iph_ext; 3498631e9bdSSteffen Klassert 3508631e9bdSSteffen Klassert ahp = x->data; 3518631e9bdSSteffen Klassert ahash = ahp->ahash; 3528631e9bdSSteffen Klassert 3538631e9bdSSteffen Klassert if ((err = skb_cow_data(skb, 0, &trailer)) < 0) 3548631e9bdSSteffen Klassert goto out; 3558631e9bdSSteffen Klassert nfrags = err; 3561da177e4SLinus Torvalds 3577b277b1aSHerbert Xu skb_push(skb, -skb_network_offset(skb)); 3588631e9bdSSteffen Klassert extlen = skb_network_header_len(skb) - sizeof(struct ipv6hdr); 3598631e9bdSSteffen Klassert if (extlen) 3608631e9bdSSteffen Klassert extlen += sizeof(*iph_ext); 3618631e9bdSSteffen Klassert 3628631e9bdSSteffen Klassert err = -ENOMEM; 3638631e9bdSSteffen Klassert iph_base = ah_alloc_tmp(ahash, nfrags, IPV6HDR_BASELEN + extlen); 3648631e9bdSSteffen Klassert if (!iph_base) 3658631e9bdSSteffen Klassert goto out; 3668631e9bdSSteffen Klassert 3678631e9bdSSteffen Klassert iph_ext = ah_tmp_ext(iph_base); 3688631e9bdSSteffen Klassert icv = ah_tmp_icv(ahash, iph_ext, extlen); 3698631e9bdSSteffen Klassert req = ah_tmp_req(ahash, icv); 3708631e9bdSSteffen Klassert sg = ah_req_sg(ahash, req); 3718631e9bdSSteffen Klassert 3728631e9bdSSteffen Klassert ah = ip_auth_hdr(skb); 3738631e9bdSSteffen Klassert memset(ah->auth_data, 0, ahp->icv_trunc_len); 3748631e9bdSSteffen Klassert 375007f0211SHerbert Xu top_iph = ipv6_hdr(skb); 3761da177e4SLinus Torvalds top_iph->payload_len = htons(skb->len - sizeof(*top_iph)); 3771da177e4SLinus Torvalds 378007f0211SHerbert Xu nexthdr = *skb_mac_header(skb); 379007f0211SHerbert Xu *skb_mac_header(skb) = IPPROTO_AH; 3801da177e4SLinus Torvalds 3811da177e4SLinus Torvalds /* When there are no extension headers, we only need to save the first 3821da177e4SLinus Torvalds * 8 bytes of the base IP header. 3831da177e4SLinus Torvalds */ 3848631e9bdSSteffen Klassert memcpy(iph_base, top_iph, IPV6HDR_BASELEN); 3851da177e4SLinus Torvalds 3861da177e4SLinus Torvalds if (extlen) { 38759fbb3a6SMasahide NAKAMURA #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 3888631e9bdSSteffen Klassert memcpy(iph_ext, &top_iph->saddr, extlen); 389e731c248SYOSHIFUJI Hideaki #else 3908631e9bdSSteffen Klassert memcpy(iph_ext, &top_iph->daddr, extlen); 391e731c248SYOSHIFUJI Hideaki #endif 39227637df9SMasahide NAKAMURA err = ipv6_clear_mutable_options(top_iph, 3938631e9bdSSteffen Klassert extlen - sizeof(*iph_ext) + 39427637df9SMasahide NAKAMURA sizeof(*top_iph), 39527637df9SMasahide NAKAMURA XFRM_POLICY_OUT); 3961da177e4SLinus Torvalds if (err) 3978631e9bdSSteffen Klassert goto out_free; 3981da177e4SLinus Torvalds } 3991da177e4SLinus Torvalds 4001da177e4SLinus Torvalds ah->nexthdr = nexthdr; 4011da177e4SLinus Torvalds 4021da177e4SLinus Torvalds top_iph->priority = 0; 4031da177e4SLinus Torvalds top_iph->flow_lbl[0] = 0; 4041da177e4SLinus Torvalds top_iph->flow_lbl[1] = 0; 4051da177e4SLinus Torvalds top_iph->flow_lbl[2] = 0; 4061da177e4SLinus Torvalds top_iph->hop_limit = 0; 4071da177e4SLinus Torvalds 40887bdc48dSHerbert Xu ah->hdrlen = (XFRM_ALIGN8(sizeof(*ah) + ahp->icv_trunc_len) >> 2) - 2; 4091da177e4SLinus Torvalds 4101da177e4SLinus Torvalds ah->reserved = 0; 4111da177e4SLinus Torvalds ah->spi = x->id.spi; 4121ce3644aSSteffen Klassert ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); 413b7c6538cSHerbert Xu 4148631e9bdSSteffen Klassert sg_init_table(sg, nfrags); 4158631e9bdSSteffen Klassert skb_to_sgvec(skb, sg, 0, skb->len); 416b7c6538cSHerbert Xu 4178631e9bdSSteffen Klassert ahash_request_set_crypt(req, sg, icv, skb->len); 4188631e9bdSSteffen Klassert ahash_request_set_callback(req, 0, ah6_output_done, skb); 4191da177e4SLinus Torvalds 4208631e9bdSSteffen Klassert AH_SKB_CB(skb)->tmp = iph_base; 4218631e9bdSSteffen Klassert 4228631e9bdSSteffen Klassert err = crypto_ahash_digest(req); 4238631e9bdSSteffen Klassert if (err) { 4248631e9bdSSteffen Klassert if (err == -EINPROGRESS) 4258631e9bdSSteffen Klassert goto out; 4268631e9bdSSteffen Klassert 4278631e9bdSSteffen Klassert if (err == -EBUSY) 4288631e9bdSSteffen Klassert err = NET_XMIT_DROP; 4298631e9bdSSteffen Klassert goto out_free; 4301da177e4SLinus Torvalds } 4311da177e4SLinus Torvalds 4328631e9bdSSteffen Klassert memcpy(ah->auth_data, icv, ahp->icv_trunc_len); 4338631e9bdSSteffen Klassert memcpy(top_iph, iph_base, IPV6HDR_BASELEN); 4348631e9bdSSteffen Klassert 4358631e9bdSSteffen Klassert if (extlen) { 4368631e9bdSSteffen Klassert #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) 4378631e9bdSSteffen Klassert memcpy(&top_iph->saddr, iph_ext, extlen); 4388631e9bdSSteffen Klassert #else 4398631e9bdSSteffen Klassert memcpy(&top_iph->daddr, iph_ext, extlen); 4408631e9bdSSteffen Klassert #endif 4418631e9bdSSteffen Klassert } 4428631e9bdSSteffen Klassert 4438631e9bdSSteffen Klassert out_free: 4448631e9bdSSteffen Klassert kfree(iph_base); 4458631e9bdSSteffen Klassert out: 4461da177e4SLinus Torvalds return err; 4471da177e4SLinus Torvalds } 4481da177e4SLinus Torvalds 4498631e9bdSSteffen Klassert static void ah6_input_done(struct crypto_async_request *base, int err) 4508631e9bdSSteffen Klassert { 4518631e9bdSSteffen Klassert u8 *auth_data; 4528631e9bdSSteffen Klassert u8 *icv; 4538631e9bdSSteffen Klassert u8 *work_iph; 4548631e9bdSSteffen Klassert struct sk_buff *skb = base->data; 4558631e9bdSSteffen Klassert struct xfrm_state *x = xfrm_input_state(skb); 4568631e9bdSSteffen Klassert struct ah_data *ahp = x->data; 4578631e9bdSSteffen Klassert struct ip_auth_hdr *ah = ip_auth_hdr(skb); 4588631e9bdSSteffen Klassert int hdr_len = skb_network_header_len(skb); 4598631e9bdSSteffen Klassert int ah_hlen = (ah->hdrlen + 2) << 2; 4608631e9bdSSteffen Klassert 4618631e9bdSSteffen Klassert work_iph = AH_SKB_CB(skb)->tmp; 4628631e9bdSSteffen Klassert auth_data = ah_tmp_auth(work_iph, hdr_len); 4638631e9bdSSteffen Klassert icv = ah_tmp_icv(ahp->ahash, auth_data, ahp->icv_trunc_len); 4648631e9bdSSteffen Klassert 4658631e9bdSSteffen Klassert err = memcmp(icv, auth_data, ahp->icv_trunc_len) ? -EBADMSG: 0; 4668631e9bdSSteffen Klassert if (err) 4678631e9bdSSteffen Klassert goto out; 4688631e9bdSSteffen Klassert 469b7ea81a5SNick Bowler err = ah->nexthdr; 470b7ea81a5SNick Bowler 4718631e9bdSSteffen Klassert skb->network_header += ah_hlen; 4728631e9bdSSteffen Klassert memcpy(skb_network_header(skb), work_iph, hdr_len); 4738631e9bdSSteffen Klassert __skb_pull(skb, ah_hlen + hdr_len); 4748631e9bdSSteffen Klassert skb_set_transport_header(skb, -hdr_len); 4758631e9bdSSteffen Klassert out: 4768631e9bdSSteffen Klassert kfree(AH_SKB_CB(skb)->tmp); 4778631e9bdSSteffen Klassert xfrm_input_resume(skb, err); 4788631e9bdSSteffen Klassert } 4798631e9bdSSteffen Klassert 4808631e9bdSSteffen Klassert 4818631e9bdSSteffen Klassert 482e695633eSHerbert Xu static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) 4831da177e4SLinus Torvalds { 4841da177e4SLinus Torvalds /* 4851da177e4SLinus Torvalds * Before process AH 4861da177e4SLinus Torvalds * [IPv6][Ext1][Ext2][AH][Dest][Payload] 4871da177e4SLinus Torvalds * |<-------------->| hdr_len 4881da177e4SLinus Torvalds * 4891da177e4SLinus Torvalds * To erase AH: 4901da177e4SLinus Torvalds * Keeping copy of cleared headers. After AH processing, 491b0e380b1SArnaldo Carvalho de Melo * Moving the pointer of skb->network_header by using skb_pull as long 492b0e380b1SArnaldo Carvalho de Melo * as AH header length. Then copy back the copy as long as hdr_len 4931da177e4SLinus Torvalds * If destination header following AH exists, copy it into after [Ext2]. 4941da177e4SLinus Torvalds * 4951da177e4SLinus Torvalds * |<>|[IPv6][Ext1][Ext2][Dest][Payload] 4961da177e4SLinus Torvalds * There is offset of AH before IPv6 header after the process. 4971da177e4SLinus Torvalds */ 4981da177e4SLinus Torvalds 4998631e9bdSSteffen Klassert u8 *auth_data; 5008631e9bdSSteffen Klassert u8 *icv; 5018631e9bdSSteffen Klassert u8 *work_iph; 5028631e9bdSSteffen Klassert struct sk_buff *trailer; 5038631e9bdSSteffen Klassert struct crypto_ahash *ahash; 5048631e9bdSSteffen Klassert struct ahash_request *req; 5058631e9bdSSteffen Klassert struct scatterlist *sg; 50687bdc48dSHerbert Xu struct ip_auth_hdr *ah; 5070660e03fSArnaldo Carvalho de Melo struct ipv6hdr *ip6h; 5081da177e4SLinus Torvalds struct ah_data *ahp; 5091da177e4SLinus Torvalds u16 hdr_len; 5101da177e4SLinus Torvalds u16 ah_hlen; 5111da177e4SLinus Torvalds int nexthdr; 5128631e9bdSSteffen Klassert int nfrags; 5138631e9bdSSteffen Klassert int err = -ENOMEM; 5141da177e4SLinus Torvalds 5151da177e4SLinus Torvalds if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr))) 5161da177e4SLinus Torvalds goto out; 5171da177e4SLinus Torvalds 5181da177e4SLinus Torvalds /* We are going to _remove_ AH header to keep sockets happy, 5191da177e4SLinus Torvalds * so... Later this can change. */ 5201da177e4SLinus Torvalds if (skb_cloned(skb) && 5211da177e4SLinus Torvalds pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) 5221da177e4SLinus Torvalds goto out; 5231da177e4SLinus Torvalds 5247aa68cb9SHerbert Xu skb->ip_summed = CHECKSUM_NONE; 5257aa68cb9SHerbert Xu 5268631e9bdSSteffen Klassert hdr_len = skb_network_header_len(skb); 52787bdc48dSHerbert Xu ah = (struct ip_auth_hdr *)skb->data; 5281da177e4SLinus Torvalds ahp = x->data; 5298631e9bdSSteffen Klassert ahash = ahp->ahash; 5308631e9bdSSteffen Klassert 5311da177e4SLinus Torvalds nexthdr = ah->nexthdr; 5321da177e4SLinus Torvalds ah_hlen = (ah->hdrlen + 2) << 2; 5331da177e4SLinus Torvalds 53487bdc48dSHerbert Xu if (ah_hlen != XFRM_ALIGN8(sizeof(*ah) + ahp->icv_full_len) && 53587bdc48dSHerbert Xu ah_hlen != XFRM_ALIGN8(sizeof(*ah) + ahp->icv_trunc_len)) 5361da177e4SLinus Torvalds goto out; 5371da177e4SLinus Torvalds 5381da177e4SLinus Torvalds if (!pskb_may_pull(skb, ah_hlen)) 5391da177e4SLinus Torvalds goto out; 5401da177e4SLinus Torvalds 5418631e9bdSSteffen Klassert 5428631e9bdSSteffen Klassert if ((err = skb_cow_data(skb, 0, &trailer)) < 0) 5438631e9bdSSteffen Klassert goto out; 5448631e9bdSSteffen Klassert nfrags = err; 5458631e9bdSSteffen Klassert 5464b0ef1f2SDang Hongwu ah = (struct ip_auth_hdr *)skb->data; 5474b0ef1f2SDang Hongwu ip6h = ipv6_hdr(skb); 5484b0ef1f2SDang Hongwu 5494b0ef1f2SDang Hongwu skb_push(skb, hdr_len); 5504b0ef1f2SDang Hongwu 5518631e9bdSSteffen Klassert work_iph = ah_alloc_tmp(ahash, nfrags, hdr_len + ahp->icv_trunc_len); 5528631e9bdSSteffen Klassert if (!work_iph) 5538631e9bdSSteffen Klassert goto out; 5548631e9bdSSteffen Klassert 5558631e9bdSSteffen Klassert auth_data = ah_tmp_auth(work_iph, hdr_len); 5568631e9bdSSteffen Klassert icv = ah_tmp_icv(ahash, auth_data, ahp->icv_trunc_len); 5578631e9bdSSteffen Klassert req = ah_tmp_req(ahash, icv); 5588631e9bdSSteffen Klassert sg = ah_req_sg(ahash, req); 5598631e9bdSSteffen Klassert 5608631e9bdSSteffen Klassert memcpy(work_iph, ip6h, hdr_len); 5618631e9bdSSteffen Klassert memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len); 5628631e9bdSSteffen Klassert memset(ah->auth_data, 0, ahp->icv_trunc_len); 5638631e9bdSSteffen Klassert 5640660e03fSArnaldo Carvalho de Melo if (ipv6_clear_mutable_options(ip6h, hdr_len, XFRM_POLICY_IN)) 5658631e9bdSSteffen Klassert goto out_free; 5668631e9bdSSteffen Klassert 5670660e03fSArnaldo Carvalho de Melo ip6h->priority = 0; 5680660e03fSArnaldo Carvalho de Melo ip6h->flow_lbl[0] = 0; 5690660e03fSArnaldo Carvalho de Melo ip6h->flow_lbl[1] = 0; 5700660e03fSArnaldo Carvalho de Melo ip6h->flow_lbl[2] = 0; 5710660e03fSArnaldo Carvalho de Melo ip6h->hop_limit = 0; 5721da177e4SLinus Torvalds 5738631e9bdSSteffen Klassert sg_init_table(sg, nfrags); 5748631e9bdSSteffen Klassert skb_to_sgvec(skb, sg, 0, skb->len); 5751da177e4SLinus Torvalds 5768631e9bdSSteffen Klassert ahash_request_set_crypt(req, sg, icv, skb->len); 5778631e9bdSSteffen Klassert ahash_request_set_callback(req, 0, ah6_input_done, skb); 5788631e9bdSSteffen Klassert 5798631e9bdSSteffen Klassert AH_SKB_CB(skb)->tmp = work_iph; 5808631e9bdSSteffen Klassert 5818631e9bdSSteffen Klassert err = crypto_ahash_digest(req); 5828631e9bdSSteffen Klassert if (err) { 5838631e9bdSSteffen Klassert if (err == -EINPROGRESS) 5848631e9bdSSteffen Klassert goto out; 5858631e9bdSSteffen Klassert 5868631e9bdSSteffen Klassert goto out_free; 5870ebea8efSHerbert Xu } 5880ebea8efSHerbert Xu 5898631e9bdSSteffen Klassert err = memcmp(icv, auth_data, ahp->icv_trunc_len) ? -EBADMSG: 0; 5900ebea8efSHerbert Xu if (err) 5918631e9bdSSteffen Klassert goto out_free; 5921da177e4SLinus Torvalds 593b0e380b1SArnaldo Carvalho de Melo skb->network_header += ah_hlen; 5948631e9bdSSteffen Klassert memcpy(skb_network_header(skb), work_iph, hdr_len); 595b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header; 59631a4ab93SHerbert Xu __skb_pull(skb, ah_hlen + hdr_len); 5971da177e4SLinus Torvalds 5988631e9bdSSteffen Klassert err = nexthdr; 5991da177e4SLinus Torvalds 6008631e9bdSSteffen Klassert out_free: 6018631e9bdSSteffen Klassert kfree(work_iph); 6021da177e4SLinus Torvalds out: 60307d4ee58SHerbert Xu return err; 6041da177e4SLinus Torvalds } 6051da177e4SLinus Torvalds 6061da177e4SLinus Torvalds static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 607d5fdd6baSBrian Haley u8 type, u8 code, int offset, __be32 info) 6081da177e4SLinus Torvalds { 6094fb236baSAlexey Dobriyan struct net *net = dev_net(skb->dev); 6101da177e4SLinus Torvalds struct ipv6hdr *iph = (struct ipv6hdr*)skb->data; 6111da177e4SLinus Torvalds struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+offset); 6121da177e4SLinus Torvalds struct xfrm_state *x; 6131da177e4SLinus Torvalds 6141da177e4SLinus Torvalds if (type != ICMPV6_DEST_UNREACH && 6151da177e4SLinus Torvalds type != ICMPV6_PKT_TOOBIG) 6161da177e4SLinus Torvalds return; 6171da177e4SLinus Torvalds 618bd55775cSJamal Hadi Salim x = xfrm_state_lookup(net, skb->mark, (xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET6); 6191da177e4SLinus Torvalds if (!x) 6201da177e4SLinus Torvalds return; 6211da177e4SLinus Torvalds 6225b095d98SHarvey Harrison NETDEBUG(KERN_DEBUG "pmtu discovery on SA AH/%08x/%pI6\n", 6230c6ce78aSHarvey Harrison ntohl(ah->spi), &iph->daddr); 6241da177e4SLinus Torvalds 6251da177e4SLinus Torvalds xfrm_state_put(x); 6261da177e4SLinus Torvalds } 6271da177e4SLinus Torvalds 62872cb6962SHerbert Xu static int ah6_init_state(struct xfrm_state *x) 6291da177e4SLinus Torvalds { 6301da177e4SLinus Torvalds struct ah_data *ahp = NULL; 6311da177e4SLinus Torvalds struct xfrm_algo_desc *aalg_desc; 6328631e9bdSSteffen Klassert struct crypto_ahash *ahash; 6331da177e4SLinus Torvalds 6341da177e4SLinus Torvalds if (!x->aalg) 6351da177e4SLinus Torvalds goto error; 6361da177e4SLinus Torvalds 6371da177e4SLinus Torvalds if (x->encap) 6381da177e4SLinus Torvalds goto error; 6391da177e4SLinus Torvalds 6400c600edaSIngo Oeser ahp = kzalloc(sizeof(*ahp), GFP_KERNEL); 6411da177e4SLinus Torvalds if (ahp == NULL) 6421da177e4SLinus Torvalds return -ENOMEM; 6431da177e4SLinus Torvalds 6448631e9bdSSteffen Klassert ahash = crypto_alloc_ahash(x->aalg->alg_name, 0, 0); 6458631e9bdSSteffen Klassert if (IS_ERR(ahash)) 6461da177e4SLinus Torvalds goto error; 64707d4ee58SHerbert Xu 6488631e9bdSSteffen Klassert ahp->ahash = ahash; 6498631e9bdSSteffen Klassert if (crypto_ahash_setkey(ahash, x->aalg->alg_key, 650bc31d3b2SHerbert Xu (x->aalg->alg_key_len + 7) / 8)) 65107d4ee58SHerbert Xu goto error; 6521da177e4SLinus Torvalds 6531da177e4SLinus Torvalds /* 6541da177e4SLinus Torvalds * Lookup the algorithm description maintained by xfrm_algo, 6551da177e4SLinus Torvalds * verify crypto transform properties, and store information 6561da177e4SLinus Torvalds * we need for AH processing. This lookup cannot fail here 65707d4ee58SHerbert Xu * after a successful crypto_alloc_hash(). 6581da177e4SLinus Torvalds */ 6591da177e4SLinus Torvalds aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0); 6601da177e4SLinus Torvalds BUG_ON(!aalg_desc); 6611da177e4SLinus Torvalds 6621da177e4SLinus Torvalds if (aalg_desc->uinfo.auth.icv_fullbits/8 != 6638631e9bdSSteffen Klassert crypto_ahash_digestsize(ahash)) { 664f3213831SJoe Perches pr_info("AH: %s digestsize %u != %hu\n", 6658631e9bdSSteffen Klassert x->aalg->alg_name, crypto_ahash_digestsize(ahash), 6661da177e4SLinus Torvalds aalg_desc->uinfo.auth.icv_fullbits/8); 6671da177e4SLinus Torvalds goto error; 6681da177e4SLinus Torvalds } 6691da177e4SLinus Torvalds 6701da177e4SLinus Torvalds ahp->icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8; 6718f8a088cSMartin Willi ahp->icv_trunc_len = x->aalg->alg_trunc_len/8; 6721da177e4SLinus Torvalds 6731da177e4SLinus Torvalds BUG_ON(ahp->icv_trunc_len > MAX_AH_AUTH_LEN); 6741da177e4SLinus Torvalds 67587bdc48dSHerbert Xu x->props.header_len = XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + 67687bdc48dSHerbert Xu ahp->icv_trunc_len); 677ca68145fSHerbert Xu switch (x->props.mode) { 678ca68145fSHerbert Xu case XFRM_MODE_BEET: 679ca68145fSHerbert Xu case XFRM_MODE_TRANSPORT: 680ca68145fSHerbert Xu break; 681ca68145fSHerbert Xu case XFRM_MODE_TUNNEL: 6821da177e4SLinus Torvalds x->props.header_len += sizeof(struct ipv6hdr); 683ea2c47b4SMasahide NAKAMURA break; 684ca68145fSHerbert Xu default: 685ca68145fSHerbert Xu goto error; 686ca68145fSHerbert Xu } 6871da177e4SLinus Torvalds x->data = ahp; 6881da177e4SLinus Torvalds 6891da177e4SLinus Torvalds return 0; 6901da177e4SLinus Torvalds 6911da177e4SLinus Torvalds error: 6921da177e4SLinus Torvalds if (ahp) { 6938631e9bdSSteffen Klassert crypto_free_ahash(ahp->ahash); 6941da177e4SLinus Torvalds kfree(ahp); 6951da177e4SLinus Torvalds } 6961da177e4SLinus Torvalds return -EINVAL; 6971da177e4SLinus Torvalds } 6981da177e4SLinus Torvalds 6991da177e4SLinus Torvalds static void ah6_destroy(struct xfrm_state *x) 7001da177e4SLinus Torvalds { 7011da177e4SLinus Torvalds struct ah_data *ahp = x->data; 7021da177e4SLinus Torvalds 7031da177e4SLinus Torvalds if (!ahp) 7041da177e4SLinus Torvalds return; 7051da177e4SLinus Torvalds 7068631e9bdSSteffen Klassert crypto_free_ahash(ahp->ahash); 7071da177e4SLinus Torvalds kfree(ahp); 7081da177e4SLinus Torvalds } 7091da177e4SLinus Torvalds 710533cb5b0SEric Dumazet static const struct xfrm_type ah6_type = 7111da177e4SLinus Torvalds { 7121da177e4SLinus Torvalds .description = "AH6", 7131da177e4SLinus Torvalds .owner = THIS_MODULE, 7141da177e4SLinus Torvalds .proto = IPPROTO_AH, 715436a0a40SHerbert Xu .flags = XFRM_TYPE_REPLAY_PROT, 7161da177e4SLinus Torvalds .init_state = ah6_init_state, 7171da177e4SLinus Torvalds .destructor = ah6_destroy, 7181da177e4SLinus Torvalds .input = ah6_input, 719aee5adb4SMasahide NAKAMURA .output = ah6_output, 720aee5adb4SMasahide NAKAMURA .hdr_offset = xfrm6_find_1stfragopt, 7211da177e4SLinus Torvalds }; 7221da177e4SLinus Torvalds 72341135cc8SAlexey Dobriyan static const struct inet6_protocol ah6_protocol = { 7241da177e4SLinus Torvalds .handler = xfrm6_rcv, 7251da177e4SLinus Torvalds .err_handler = ah6_err, 7261da177e4SLinus Torvalds .flags = INET6_PROTO_NOPOLICY, 7271da177e4SLinus Torvalds }; 7281da177e4SLinus Torvalds 7291da177e4SLinus Torvalds static int __init ah6_init(void) 7301da177e4SLinus Torvalds { 7311da177e4SLinus Torvalds if (xfrm_register_type(&ah6_type, AF_INET6) < 0) { 732f3213831SJoe Perches pr_info("%s: can't add xfrm type\n", __func__); 7331da177e4SLinus Torvalds return -EAGAIN; 7341da177e4SLinus Torvalds } 7351da177e4SLinus Torvalds 7361da177e4SLinus Torvalds if (inet6_add_protocol(&ah6_protocol, IPPROTO_AH) < 0) { 737f3213831SJoe Perches pr_info("%s: can't add protocol\n", __func__); 7381da177e4SLinus Torvalds xfrm_unregister_type(&ah6_type, AF_INET6); 7391da177e4SLinus Torvalds return -EAGAIN; 7401da177e4SLinus Torvalds } 7411da177e4SLinus Torvalds 7421da177e4SLinus Torvalds return 0; 7431da177e4SLinus Torvalds } 7441da177e4SLinus Torvalds 7451da177e4SLinus Torvalds static void __exit ah6_fini(void) 7461da177e4SLinus Torvalds { 7471da177e4SLinus Torvalds if (inet6_del_protocol(&ah6_protocol, IPPROTO_AH) < 0) 748f3213831SJoe Perches pr_info("%s: can't remove protocol\n", __func__); 7491da177e4SLinus Torvalds 7501da177e4SLinus Torvalds if (xfrm_unregister_type(&ah6_type, AF_INET6) < 0) 751f3213831SJoe Perches pr_info("%s: can't remove xfrm type\n", __func__); 7521da177e4SLinus Torvalds 7531da177e4SLinus Torvalds } 7541da177e4SLinus Torvalds 7551da177e4SLinus Torvalds module_init(ah6_init); 7561da177e4SLinus Torvalds module_exit(ah6_fini); 7571da177e4SLinus Torvalds 7581da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 759d3d6dd3aSMasahide NAKAMURA MODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_AH); 760