| 1 | /* |
| 2 | * Copyright (c) 2008-2011 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
| 5 | * |
| 6 | * This file contains Original Code and/or Modifications of Original Code |
| 7 | * as defined in and that are subject to the Apple Public Source License |
| 8 | * Version 2.0 (the 'License'). You may not use this file except in |
| 9 | * compliance with the License. The rights granted to you under the License |
| 10 | * may not be used to create, or enable the creation or redistribution of, |
| 11 | * unlawful or unlicensed copies of an Apple operating system, or to |
| 12 | * circumvent, violate, or enable the circumvention or violation of, any |
| 13 | * terms of an Apple operating system software license agreement. |
| 14 | * |
| 15 | * Please obtain a copy of the License at |
| 16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
| 17 | * |
| 18 | * The Original Code and all software distributed under the License are |
| 19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
| 20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
| 21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
| 22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
| 23 | * Please see the License for the specific language governing rights and |
| 24 | * limitations under the License. |
| 25 | * |
| 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
| 27 | */ |
| 28 | |
| 29 | /* $FreeBSD: src/sys/netinet6/ah_output.c,v 1.1.2.3 2001/07/03 11:01:49 ume Exp $ */ |
| 30 | /* $KAME: ah_output.c,v 1.30 2001/02/21 00:50:53 itojun Exp $ */ |
| 31 | |
| 32 | /* |
| 33 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
| 34 | * All rights reserved. |
| 35 | * |
| 36 | * Redistribution and use in source and binary forms, with or without |
| 37 | * modification, are permitted provided that the following conditions |
| 38 | * are met: |
| 39 | * 1. Redistributions of source code must retain the above copyright |
| 40 | * notice, this list of conditions and the following disclaimer. |
| 41 | * 2. Redistributions in binary form must reproduce the above copyright |
| 42 | * notice, this list of conditions and the following disclaimer in the |
| 43 | * documentation and/or other materials provided with the distribution. |
| 44 | * 3. Neither the name of the project nor the names of its contributors |
| 45 | * may be used to endorse or promote products derived from this software |
| 46 | * without specific prior written permission. |
| 47 | * |
| 48 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
| 49 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 50 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 51 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
| 52 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 53 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 54 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 55 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 56 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 57 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 58 | * SUCH DAMAGE. |
| 59 | */ |
| 60 | |
| 61 | /* |
| 62 | * RFC1826/2402 authentication header. |
| 63 | */ |
| 64 | |
| 65 | #define _IP_VHL |
| 66 | |
| 67 | #include <sys/param.h> |
| 68 | #include <sys/systm.h> |
| 69 | #include <sys/malloc.h> |
| 70 | #include <sys/mbuf.h> |
| 71 | #include <sys/domain.h> |
| 72 | #include <sys/protosw.h> |
| 73 | #include <sys/socket.h> |
| 74 | #include <sys/socketvar.h> |
| 75 | #include <sys/errno.h> |
| 76 | #include <sys/time.h> |
| 77 | #include <sys/kernel.h> |
| 78 | #include <sys/syslog.h> |
| 79 | |
| 80 | #include <net/if.h> |
| 81 | #include <net/route.h> |
| 82 | |
| 83 | #include <netinet/in.h> |
| 84 | |
| 85 | #include <netinet/in_systm.h> |
| 86 | #include <netinet/ip.h> |
| 87 | #include <netinet/in_var.h> |
| 88 | |
| 89 | #if INET6 |
| 90 | #include <netinet/ip6.h> |
| 91 | #include <netinet6/ip6_var.h> |
| 92 | #include <netinet/icmp6.h> |
| 93 | #endif |
| 94 | |
| 95 | #include <netinet6/ipsec.h> |
| 96 | #if INET6 |
| 97 | #include <netinet6/ipsec6.h> |
| 98 | #endif |
| 99 | #include <netinet6/ah.h> |
| 100 | #if INET6 |
| 101 | #include <netinet6/ah6.h> |
| 102 | #endif |
| 103 | #include <netkey/key.h> |
| 104 | #include <netkey/keydb.h> |
| 105 | |
| 106 | #include <net/net_osdep.h> |
| 107 | |
| 108 | #if INET |
| 109 | static struct in_addr *ah4_finaldst(struct mbuf *); |
| 110 | #endif |
| 111 | |
| 112 | extern lck_mtx_t *sadb_mutex; |
| 113 | |
| 114 | /* |
| 115 | * compute AH header size. |
| 116 | * transport mode only. for tunnel mode, we should implement |
| 117 | * virtual interface, and control MTU/MSS by the interface MTU. |
| 118 | */ |
| 119 | size_t |
| 120 | ah_hdrsiz(struct ipsecrequest *isr) |
| 121 | { |
| 122 | |
| 123 | /* sanity check */ |
| 124 | if (isr == NULL) |
| 125 | panic("ah_hdrsiz: NULL was passed.\n" ); |
| 126 | |
| 127 | if (isr->saidx.proto != IPPROTO_AH) |
| 128 | panic("unsupported mode passed to ah_hdrsiz" ); |
| 129 | |
| 130 | #if 0 |
| 131 | { |
| 132 | |
| 133 | lck_mtx_lock(sadb_mutex); |
| 134 | const struct ah_algorithm *algo; |
| 135 | size_t hdrsiz; |
| 136 | |
| 137 | /*%%%%% this needs to change - no sav in ipsecrequest any more */ |
| 138 | if (isr->sav == NULL) |
| 139 | goto estimate; |
| 140 | if (isr->sav->state != SADB_SASTATE_MATURE |
| 141 | && isr->sav->state != SADB_SASTATE_DYING) |
| 142 | goto estimate; |
| 143 | |
| 144 | /* we need transport mode AH. */ |
| 145 | algo = ah_algorithm_lookup(isr->sav->alg_auth); |
| 146 | if (!algo) |
| 147 | goto estimate; |
| 148 | |
| 149 | /* |
| 150 | * XXX |
| 151 | * right now we don't calcurate the padding size. simply |
| 152 | * treat the padding size as constant, for simplicity. |
| 153 | * |
| 154 | * XXX variable size padding support |
| 155 | */ |
| 156 | hdrsiz = (((*algo->sumsiz)(isr->sav) + 3) & ~(4 - 1)); |
| 157 | if (isr->sav->flags & SADB_X_EXT_OLD) |
| 158 | hdrsiz += sizeof(struct ah); |
| 159 | else |
| 160 | hdrsiz += sizeof(struct newah); |
| 161 | |
| 162 | lck_mtx_unlock(sadb_mutex); |
| 163 | return hdrsiz; |
| 164 | } |
| 165 | |
| 166 | estimate: |
| 167 | #endif |
| 168 | |
| 169 | //lck_mtx_unlock(sadb_mutex); |
| 170 | /* ASSUMING: |
| 171 | * sizeof(struct newah) > sizeof(struct ah). |
| 172 | * 16 = (16 + 3) & ~(4 - 1). |
| 173 | */ |
| 174 | return sizeof(struct newah) + 16; |
| 175 | } |
| 176 | |
| 177 | #if INET |
| 178 | /* |
| 179 | * Modify the packet so that it includes the authentication data. |
| 180 | * The mbuf passed must start with IPv4 header. |
| 181 | * |
| 182 | * assumes that the first mbuf contains IPv4 header + option only. |
| 183 | * the function does not modify m. |
| 184 | */ |
| 185 | int |
| 186 | ah4_output(struct mbuf *m, struct secasvar *sav) |
| 187 | { |
| 188 | const struct ah_algorithm *algo; |
| 189 | u_int32_t spi; |
| 190 | u_char *ahdrpos; |
| 191 | u_char *ahsumpos = NULL; |
| 192 | size_t hlen = 0; /*IP header+option in bytes*/ |
| 193 | size_t plen = 0; /*AH payload size in bytes*/ |
| 194 | size_t ahlen = 0; /*plen + sizeof(ah)*/ |
| 195 | struct ip *ip; |
| 196 | struct in_addr dst = { 0 }; |
| 197 | struct in_addr *finaldst; |
| 198 | int error; |
| 199 | |
| 200 | /* sanity checks */ |
| 201 | if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) { |
| 202 | ip = mtod(m, struct ip *); |
| 203 | ipseclog((LOG_DEBUG, "ah4_output: internal error: " |
| 204 | "sav->replay is null: %x->%x, SPI=%u\n" , |
| 205 | (u_int32_t)ntohl(ip->ip_src.s_addr), |
| 206 | (u_int32_t)ntohl(ip->ip_dst.s_addr), |
| 207 | (u_int32_t)ntohl(sav->spi))); |
| 208 | IPSEC_STAT_INCREMENT(ipsecstat.out_inval); |
| 209 | m_freem(m); |
| 210 | return EINVAL; |
| 211 | } |
| 212 | |
| 213 | algo = ah_algorithm_lookup(sav->alg_auth); |
| 214 | if (!algo) { |
| 215 | ipseclog((LOG_ERR, "ah4_output: unsupported algorithm: " |
| 216 | "SPI=%u\n" , (u_int32_t)ntohl(sav->spi))); |
| 217 | IPSEC_STAT_INCREMENT(ipsecstat.out_inval); |
| 218 | m_freem(m); |
| 219 | return EINVAL; |
| 220 | } |
| 221 | spi = sav->spi; |
| 222 | |
| 223 | /* |
| 224 | * determine the size to grow. |
| 225 | */ |
| 226 | if (sav->flags & SADB_X_EXT_OLD) { |
| 227 | /* RFC 1826 */ |
| 228 | plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ |
| 229 | ahlen = plen + sizeof(struct ah); |
| 230 | } else { |
| 231 | /* RFC 2402 */ |
| 232 | plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ |
| 233 | ahlen = plen + sizeof(struct newah); |
| 234 | } |
| 235 | |
| 236 | /* |
| 237 | * grow the mbuf to accomodate AH. |
| 238 | */ |
| 239 | ip = mtod(m, struct ip *); |
| 240 | #ifdef _IP_VHL |
| 241 | hlen = IP_VHL_HL(ip->ip_vhl) << 2; |
| 242 | #else |
| 243 | hlen = ip->ip_hl << 2; |
| 244 | #endif |
| 245 | |
| 246 | if (m->m_len != hlen) |
| 247 | panic("ah4_output: assumption failed (first mbuf length)" ); |
| 248 | if (M_LEADINGSPACE(m->m_next) < ahlen) { |
| 249 | struct mbuf *n; |
| 250 | MGET(n, M_DONTWAIT, MT_DATA); |
| 251 | if (!n) { |
| 252 | ipseclog((LOG_DEBUG, "ENOBUFS in ah4_output %d\n" , |
| 253 | __LINE__)); |
| 254 | m_freem(m); |
| 255 | return ENOBUFS; |
| 256 | } |
| 257 | n->m_len = ahlen; |
| 258 | n->m_next = m->m_next; |
| 259 | m->m_next = n; |
| 260 | m->m_pkthdr.len += ahlen; |
| 261 | ahdrpos = mtod(n, u_char *); |
| 262 | } else { |
| 263 | m->m_next->m_len += ahlen; |
| 264 | m->m_next->m_data -= ahlen; |
| 265 | m->m_pkthdr.len += ahlen; |
| 266 | ahdrpos = mtod(m->m_next, u_char *); |
| 267 | } |
| 268 | |
| 269 | ip = mtod(m, struct ip *); /*just to be sure*/ |
| 270 | |
| 271 | /* |
| 272 | * initialize AH. |
| 273 | */ |
| 274 | if (sav->flags & SADB_X_EXT_OLD) { |
| 275 | struct ah *ahdr; |
| 276 | |
| 277 | ahdr = (struct ah *)(void *)ahdrpos; |
| 278 | ahsumpos = (u_char *)(ahdr + 1); |
| 279 | ahdr->ah_len = plen >> 2; |
| 280 | ahdr->ah_nxt = ip->ip_p; |
| 281 | ahdr->ah_reserve = htons(0); |
| 282 | ahdr->ah_spi = spi; |
| 283 | bzero(ahdr + 1, plen); |
| 284 | } else { |
| 285 | struct newah *ahdr; |
| 286 | |
| 287 | ahdr = (struct newah *)(void *)ahdrpos; |
| 288 | ahsumpos = (u_char *)(ahdr + 1); |
| 289 | ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */ |
| 290 | ahdr->ah_nxt = ip->ip_p; |
| 291 | ahdr->ah_reserve = htons(0); |
| 292 | ahdr->ah_spi = spi; |
| 293 | if (sav->replay->count == ~0) { |
| 294 | if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) { |
| 295 | /* XXX Is it noisy ? */ |
| 296 | ipseclog((LOG_WARNING, |
| 297 | "replay counter overflowed. %s\n" , |
| 298 | ipsec_logsastr(sav))); |
| 299 | IPSEC_STAT_INCREMENT(ipsecstat.out_inval); |
| 300 | m_freem(m); |
| 301 | return EINVAL; |
| 302 | } |
| 303 | } |
| 304 | lck_mtx_lock(sadb_mutex); |
| 305 | sav->replay->count++; |
| 306 | lck_mtx_unlock(sadb_mutex); |
| 307 | /* |
| 308 | * XXX sequence number must not be cycled, if the SA is |
| 309 | * installed by IKE daemon. |
| 310 | */ |
| 311 | ahdr->ah_seq = htonl(sav->replay->count); |
| 312 | bzero(ahdr + 1, plen); |
| 313 | } |
| 314 | |
| 315 | /* |
| 316 | * modify IPv4 header. |
| 317 | */ |
| 318 | ip->ip_p = IPPROTO_AH; |
| 319 | if (ahlen < (IP_MAXPACKET - ntohs(ip->ip_len))) |
| 320 | ip->ip_len = htons(ntohs(ip->ip_len) + ahlen); |
| 321 | else { |
| 322 | ipseclog((LOG_ERR, "IPv4 AH output: size exceeds limit\n" )); |
| 323 | IPSEC_STAT_INCREMENT(ipsecstat.out_inval); |
| 324 | m_freem(m); |
| 325 | return EMSGSIZE; |
| 326 | } |
| 327 | |
| 328 | /* |
| 329 | * If there is source routing option, update destination field in |
| 330 | * the IPv4 header to the final destination. |
| 331 | * Note that we do not need to update source routing option itself |
| 332 | * (as done in IPv4 AH processing -- see ip6_output()), since |
| 333 | * source routing option is not part of the ICV computation. |
| 334 | */ |
| 335 | finaldst = ah4_finaldst(m); |
| 336 | if (finaldst) { |
| 337 | dst.s_addr = ip->ip_dst.s_addr; |
| 338 | ip->ip_dst.s_addr = finaldst->s_addr; |
| 339 | } |
| 340 | |
| 341 | /* |
| 342 | * calcurate the checksum, based on security association |
| 343 | * and the algorithm specified. |
| 344 | */ |
| 345 | error = ah4_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav); |
| 346 | if (error) { |
| 347 | ipseclog((LOG_ERR, |
| 348 | "error after ah4_calccksum, called from ah4_output" )); |
| 349 | m_freem(m); |
| 350 | m = NULL; |
| 351 | IPSEC_STAT_INCREMENT(ipsecstat.out_inval); |
| 352 | return error; |
| 353 | } |
| 354 | |
| 355 | if (finaldst) { |
| 356 | ip = mtod(m, struct ip *); /*just to make sure*/ |
| 357 | ip->ip_dst.s_addr = dst.s_addr; |
| 358 | } |
| 359 | lck_mtx_lock(sadb_stat_mutex); |
| 360 | ipsecstat.out_success++; |
| 361 | ipsecstat.out_ahhist[sav->alg_auth]++; |
| 362 | lck_mtx_unlock(sadb_stat_mutex); |
| 363 | key_sa_recordxfer(sav, m); |
| 364 | |
| 365 | return 0; |
| 366 | } |
| 367 | #endif |
| 368 | |
| 369 | /* Calculate AH length */ |
| 370 | int |
| 371 | ah_hdrlen(struct secasvar *sav) |
| 372 | { |
| 373 | const struct ah_algorithm *algo; |
| 374 | int plen, ahlen; |
| 375 | |
| 376 | algo = ah_algorithm_lookup(sav->alg_auth); |
| 377 | if (!algo) |
| 378 | return 0; |
| 379 | if (sav->flags & SADB_X_EXT_OLD) { |
| 380 | /* RFC 1826 */ |
| 381 | plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ |
| 382 | ahlen = plen + sizeof(struct ah); |
| 383 | } else { |
| 384 | /* RFC 2402 */ |
| 385 | plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ |
| 386 | ahlen = plen + sizeof(struct newah); |
| 387 | } |
| 388 | |
| 389 | return(ahlen); |
| 390 | } |
| 391 | |
| 392 | #if INET6 |
| 393 | /* |
| 394 | * Fill in the Authentication Header and calculate checksum. |
| 395 | */ |
| 396 | int |
| 397 | ah6_output(struct mbuf *m, u_char *nexthdrp, struct mbuf *md, |
| 398 | struct secasvar *sav) |
| 399 | { |
| 400 | struct mbuf *mprev; |
| 401 | struct mbuf *mah; |
| 402 | const struct ah_algorithm *algo; |
| 403 | u_int32_t spi; |
| 404 | u_char *ahsumpos = NULL; |
| 405 | size_t plen; /*AH payload size in bytes*/ |
| 406 | int error = 0; |
| 407 | int ahlen; |
| 408 | struct ip6_hdr *ip6; |
| 409 | |
| 410 | if (m->m_len < sizeof(struct ip6_hdr)) { |
| 411 | ipseclog((LOG_DEBUG, "ah6_output: first mbuf too short\n" )); |
| 412 | m_freem(m); |
| 413 | return EINVAL; |
| 414 | } |
| 415 | |
| 416 | ahlen = ah_hdrlen(sav); |
| 417 | if (ahlen == 0) |
| 418 | return 0; |
| 419 | |
| 420 | for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) |
| 421 | ; |
| 422 | if (!mprev || mprev->m_next != md) { |
| 423 | ipseclog((LOG_DEBUG, "ah6_output: md is not in chain\n" )); |
| 424 | m_freem(m); |
| 425 | return EINVAL; |
| 426 | } |
| 427 | |
| 428 | MGET(mah, M_DONTWAIT, MT_DATA); |
| 429 | if (!mah) { |
| 430 | m_freem(m); |
| 431 | return ENOBUFS; |
| 432 | } |
| 433 | if (ahlen > MLEN) { |
| 434 | MCLGET(mah, M_DONTWAIT); |
| 435 | if ((mah->m_flags & M_EXT) == 0) { |
| 436 | m_free(mah); |
| 437 | m_freem(m); |
| 438 | return ENOBUFS; |
| 439 | } |
| 440 | } |
| 441 | mah->m_len = ahlen; |
| 442 | mah->m_next = md; |
| 443 | mprev->m_next = mah; |
| 444 | m->m_pkthdr.len += ahlen; |
| 445 | |
| 446 | /* fix plen */ |
| 447 | if (m->m_pkthdr.len - sizeof(struct ip6_hdr) > IPV6_MAXPACKET) { |
| 448 | ipseclog((LOG_ERR, |
| 449 | "ip6_output: AH with IPv6 jumbogram is not supported\n" )); |
| 450 | m_freem(m); |
| 451 | return EINVAL; |
| 452 | } |
| 453 | ip6 = mtod(m, struct ip6_hdr *); |
| 454 | ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); |
| 455 | |
| 456 | if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) { |
| 457 | ipseclog((LOG_DEBUG, "ah6_output: internal error: " |
| 458 | "sav->replay is null: SPI=%u\n" , |
| 459 | (u_int32_t)ntohl(sav->spi))); |
| 460 | IPSEC_STAT_INCREMENT(ipsec6stat.out_inval); |
| 461 | m_freem(m); |
| 462 | return EINVAL; |
| 463 | } |
| 464 | |
| 465 | algo = ah_algorithm_lookup(sav->alg_auth); |
| 466 | if (!algo) { |
| 467 | ipseclog((LOG_ERR, "ah6_output: unsupported algorithm: " |
| 468 | "SPI=%u\n" , (u_int32_t)ntohl(sav->spi))); |
| 469 | IPSEC_STAT_INCREMENT(ipsec6stat.out_inval); |
| 470 | m_freem(m); |
| 471 | return EINVAL; |
| 472 | } |
| 473 | spi = sav->spi; |
| 474 | |
| 475 | /* |
| 476 | * initialize AH. |
| 477 | */ |
| 478 | if (sav->flags & SADB_X_EXT_OLD) { |
| 479 | struct ah *ahdr = mtod(mah, struct ah *); |
| 480 | |
| 481 | plen = mah->m_len - sizeof(struct ah); |
| 482 | ahsumpos = (u_char *)(ahdr + 1); |
| 483 | ahdr->ah_nxt = *nexthdrp; |
| 484 | *nexthdrp = IPPROTO_AH; |
| 485 | ahdr->ah_len = plen >> 2; |
| 486 | ahdr->ah_reserve = htons(0); |
| 487 | ahdr->ah_spi = spi; |
| 488 | bzero(ahdr + 1, plen); |
| 489 | } else { |
| 490 | struct newah *ahdr = mtod(mah, struct newah *); |
| 491 | |
| 492 | plen = mah->m_len - sizeof(struct newah); |
| 493 | ahsumpos = (u_char *)(ahdr + 1); |
| 494 | ahdr->ah_nxt = *nexthdrp; |
| 495 | *nexthdrp = IPPROTO_AH; |
| 496 | ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */ |
| 497 | ahdr->ah_reserve = htons(0); |
| 498 | ahdr->ah_spi = spi; |
| 499 | if (sav->replay->count == ~0) { |
| 500 | if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) { |
| 501 | /* XXX Is it noisy ? */ |
| 502 | ipseclog((LOG_WARNING, |
| 503 | "replay counter overflowed. %s\n" , |
| 504 | ipsec_logsastr(sav))); |
| 505 | IPSEC_STAT_INCREMENT(ipsec6stat.out_inval); |
| 506 | m_freem(m); |
| 507 | return EINVAL; |
| 508 | } |
| 509 | } |
| 510 | lck_mtx_lock(sadb_mutex); |
| 511 | sav->replay->count++; |
| 512 | lck_mtx_unlock(sadb_mutex); |
| 513 | /* |
| 514 | * XXX sequence number must not be cycled, if the SA is |
| 515 | * installed by IKE daemon. |
| 516 | */ |
| 517 | ahdr->ah_seq = htonl(sav->replay->count); |
| 518 | bzero(ahdr + 1, plen); |
| 519 | } |
| 520 | |
| 521 | /* |
| 522 | * calcurate the checksum, based on security association |
| 523 | * and the algorithm specified. |
| 524 | */ |
| 525 | error = ah6_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav); |
| 526 | if (error) { |
| 527 | IPSEC_STAT_INCREMENT(ipsec6stat.out_inval); |
| 528 | m_freem(m); |
| 529 | } else { |
| 530 | IPSEC_STAT_INCREMENT(ipsec6stat.out_success); |
| 531 | key_sa_recordxfer(sav, m); |
| 532 | } |
| 533 | IPSEC_STAT_INCREMENT(ipsec6stat.out_ahhist[sav->alg_auth]); |
| 534 | |
| 535 | return(error); |
| 536 | } |
| 537 | #endif |
| 538 | |
| 539 | #if INET |
| 540 | /* |
| 541 | * Find the final destination if there is loose/strict source routing option. |
| 542 | * Returns NULL if there's no source routing options. |
| 543 | * Returns NULL on errors too. |
| 544 | * Note that this function will return a pointer INTO the given parameter, |
| 545 | * struct mbuf *m. |
| 546 | * The mbuf must be pulled up toward, at least, ip option part. |
| 547 | */ |
| 548 | static struct in_addr * |
| 549 | ah4_finaldst(struct mbuf *m) |
| 550 | { |
| 551 | struct ip *ip; |
| 552 | int optlen; |
| 553 | u_char *q; |
| 554 | int i; |
| 555 | int hlen; |
| 556 | |
| 557 | if (!m) |
| 558 | panic("ah4_finaldst: m == NULL" ); |
| 559 | ip = mtod(m, struct ip *); |
| 560 | #ifdef _IP_VHL |
| 561 | hlen = IP_VHL_HL(ip->ip_vhl) << 2; |
| 562 | #else |
| 563 | hlen = ip->ip_hl << 2; |
| 564 | #endif |
| 565 | |
| 566 | if (m->m_len < hlen) { |
| 567 | ipseclog((LOG_DEBUG, |
| 568 | "ah4_finaldst: parameter mbuf wrong (not pulled up)\n" )); |
| 569 | return NULL; |
| 570 | } |
| 571 | |
| 572 | if (hlen == sizeof(struct ip)) |
| 573 | return NULL; |
| 574 | |
| 575 | optlen = hlen - sizeof(struct ip); |
| 576 | if (optlen < 0) { |
| 577 | ipseclog((LOG_DEBUG, "ah4_finaldst: wrong optlen %d\n" , |
| 578 | optlen)); |
| 579 | return NULL; |
| 580 | } |
| 581 | |
| 582 | q = (u_char *)(ip + 1); |
| 583 | i = 0; |
| 584 | while (i < optlen) { |
| 585 | if (i + IPOPT_OPTVAL >= optlen) |
| 586 | return NULL; |
| 587 | if (q[i + IPOPT_OPTVAL] == IPOPT_EOL || |
| 588 | q[i + IPOPT_OPTVAL] == IPOPT_NOP || |
| 589 | i + IPOPT_OLEN < optlen) |
| 590 | ; |
| 591 | else |
| 592 | return NULL; |
| 593 | |
| 594 | switch (q[i + IPOPT_OPTVAL]) { |
| 595 | case IPOPT_EOL: |
| 596 | i = optlen; /* bye */ |
| 597 | break; |
| 598 | case IPOPT_NOP: |
| 599 | i++; |
| 600 | break; |
| 601 | case IPOPT_LSRR: |
| 602 | case IPOPT_SSRR: |
| 603 | if (q[i + IPOPT_OLEN] < 2 + sizeof(struct in_addr) || |
| 604 | optlen - i < q[i + IPOPT_OLEN]) { |
| 605 | ipseclog((LOG_ERR, |
| 606 | "ip_finaldst: invalid IP option " |
| 607 | "(code=%02x len=%02x)\n" , |
| 608 | q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN])); |
| 609 | return NULL; |
| 610 | } |
| 611 | i += q[i + IPOPT_OLEN] - sizeof(struct in_addr); |
| 612 | return (struct in_addr *)(void *)(q + i); |
| 613 | default: |
| 614 | if (q[i + IPOPT_OLEN] < 2 || |
| 615 | optlen - i < q[i + IPOPT_OLEN]) { |
| 616 | ipseclog((LOG_ERR, |
| 617 | "ip_finaldst: invalid IP option " |
| 618 | "(code=%02x len=%02x)\n" , |
| 619 | q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN])); |
| 620 | return NULL; |
| 621 | } |
| 622 | i += q[i + IPOPT_OLEN]; |
| 623 | break; |
| 624 | } |
| 625 | } |
| 626 | return NULL; |
| 627 | } |
| 628 | #endif |
| 629 | |