Pre-2.0 release: Sync with HAMMER 65 - simplify PFS operations.
[dragonfly.git] / sys / netinet6 / ah_input.c
bloba1cddae9e4c39bc052845657af81d753a4ea253f
1 /* $FreeBSD: src/sys/netinet6/ah_input.c,v 1.1.2.6 2002/04/28 05:40:26 suz Exp $ */
2 /* $DragonFly: src/sys/netinet6/ah_input.c,v 1.14 2007/11/26 11:43:09 sephe Exp $ */
3 /* $KAME: ah_input.c,v 1.67 2002/01/07 11:39:56 kjc Exp $ */
5 /*
6 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7 * All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the project nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
35 * RFC1826/2402 authentication header.
38 #include "opt_inet.h"
39 #include "opt_inet6.h"
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/malloc.h>
44 #include <sys/mbuf.h>
45 #include <sys/domain.h>
46 #include <sys/protosw.h>
47 #include <sys/socket.h>
48 #include <sys/errno.h>
49 #include <sys/time.h>
50 #include <sys/syslog.h>
52 #include <net/if.h>
53 #include <net/route.h>
54 #include <net/netisr.h>
55 #include <machine/cpu.h>
56 #include <machine/stdarg.h>
58 #include <netinet/in.h>
59 #include <netinet/in_systm.h>
60 #include <netinet/in_var.h>
61 #include <netinet/ip.h>
62 #include <netinet/ip_var.h>
63 #include <netinet/ip_ecn.h>
64 #ifdef INET6
65 #include <netinet6/ip6_ecn.h>
66 #endif
68 #ifdef INET6
69 #include <netinet/ip6.h>
70 #include <netinet6/ip6_var.h>
71 #include <netinet6/in6_pcb.h>
72 #include <netinet/icmp6.h>
73 #include <netinet6/ip6protosw.h>
74 #endif
76 #include <netinet6/ipsec.h>
77 #ifdef INET6
78 #include <netinet6/ipsec6.h>
79 #endif
80 #include <netinet6/ah.h>
81 #ifdef INET6
82 #include <netinet6/ah6.h>
83 #endif
84 #include <netproto/key/key.h>
85 #include <netproto/key/keydb.h>
86 #ifdef IPSEC_DEBUG
87 #include <netproto/key/key_debug.h>
88 #else
89 #define KEYDEBUG(lev,arg)
90 #endif
92 #include <machine/stdarg.h>
94 #include <net/net_osdep.h>
96 #define IPLEN_FLIPPED
98 #ifdef INET
99 extern struct protosw inetsw[];
101 void
102 ah4_input(struct mbuf *m, ...)
104 int off, proto;
105 struct ip *ip;
106 struct ah *ah;
107 u_int32_t spi;
108 const struct ah_algorithm *algo;
109 size_t siz;
110 size_t siz1;
111 u_char *cksum;
112 struct secasvar *sav = NULL;
113 u_int16_t nxt;
114 size_t hlen;
115 size_t stripsiz = 0;
116 __va_list ap;
118 __va_start(ap, m);
119 off = __va_arg(ap, int);
120 proto = __va_arg(ap, int);
121 __va_end(ap);
123 #ifndef PULLDOWN_TEST
124 if (m->m_len < off + sizeof(struct newah)) {
125 m = m_pullup(m, off + sizeof(struct newah));
126 if (!m) {
127 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;"
128 "dropping the packet for simplicity\n"));
129 ipsecstat.in_inval++;
130 goto fail;
134 ip = mtod(m, struct ip *);
135 ah = (struct ah *)(((caddr_t)ip) + off);
136 #else
137 ip = mtod(m, struct ip *);
138 IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
139 if (ah == NULL) {
140 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;"
141 "dropping the packet for simplicity\n"));
142 ipsecstat.in_inval++;
143 goto fail;
145 #endif
146 nxt = ah->ah_nxt;
147 #ifdef _IP_VHL
148 hlen = IP_VHL_HL(ip->ip_vhl) << 2;
149 #else
150 hlen = ip->ip_hl << 2;
151 #endif
153 /* find the sassoc. */
154 spi = ah->ah_spi;
156 if ((sav = key_allocsa(AF_INET,
157 (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
158 IPPROTO_AH, spi)) == 0) {
159 ipseclog((LOG_WARNING,
160 "IPv4 AH input: no key association found for spi %u\n",
161 (u_int32_t)ntohl(spi)));
162 ipsecstat.in_nosa++;
163 goto fail;
165 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
166 kprintf("DP ah4_input called to allocate SA:%p\n", sav));
167 if (sav->state != SADB_SASTATE_MATURE
168 && sav->state != SADB_SASTATE_DYING) {
169 ipseclog((LOG_DEBUG,
170 "IPv4 AH input: non-mature/dying SA found for spi %u\n",
171 (u_int32_t)ntohl(spi)));
172 ipsecstat.in_badspi++;
173 goto fail;
176 algo = ah_algorithm_lookup(sav->alg_auth);
177 if (!algo) {
178 ipseclog((LOG_DEBUG, "IPv4 AH input: "
179 "unsupported authentication algorithm for spi %u\n",
180 (u_int32_t)ntohl(spi)));
181 ipsecstat.in_badspi++;
182 goto fail;
185 siz = (*algo->sumsiz)(sav);
186 siz1 = ((siz + 3) & ~(4 - 1));
189 * sanity checks for header, 1.
192 int sizoff;
194 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
197 * Here, we do not do "siz1 == siz". This is because the way
198 * RFC240[34] section 2 is written. They do not require truncation
199 * to 96 bits.
200 * For example, Microsoft IPsec stack attaches 160 bits of
201 * authentication data for both hmac-md5 and hmac-sha1. For hmac-sha1,
202 * 32 bits of padding is attached.
204 * There are two downsides to this specification.
205 * They have no real harm, however, they leave us fuzzy feeling.
206 * - if we attach more than 96 bits of authentication data onto AH,
207 * we will never notice about possible modification by rogue
208 * intermediate nodes.
209 * Since extra bits in AH checksum is never used, this constitutes
210 * no real issue, however, it is wacky.
211 * - even if the peer attaches big authentication data, we will never
212 * notice the difference, since longer authentication data will just
213 * work.
215 * We may need some clarification in the spec.
217 if (siz1 < siz) {
218 ipseclog((LOG_NOTICE, "sum length too short in IPv4 AH input "
219 "(%lu, should be at least %lu): %s\n",
220 (u_long)siz1, (u_long)siz,
221 ipsec4_logpacketstr(ip, spi)));
222 ipsecstat.in_inval++;
223 goto fail;
225 if ((ah->ah_len << 2) - sizoff != siz1) {
226 ipseclog((LOG_NOTICE, "sum length mismatch in IPv4 AH input "
227 "(%d should be %lu): %s\n",
228 (ah->ah_len << 2) - sizoff, (u_long)siz1,
229 ipsec4_logpacketstr(ip, spi)));
230 ipsecstat.in_inval++;
231 goto fail;
234 #ifndef PULLDOWN_TEST
235 if (m->m_len < off + sizeof(struct ah) + sizoff + siz1) {
236 m = m_pullup(m, off + sizeof(struct ah) + sizoff + siz1);
237 if (!m) {
238 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n"));
239 ipsecstat.in_inval++;
240 goto fail;
243 ip = mtod(m, struct ip *);
244 ah = (struct ah *)(((caddr_t)ip) + off);
246 #else
247 IP6_EXTHDR_GET(ah, struct ah *, m, off,
248 sizeof(struct ah) + sizoff + siz1);
249 if (ah == NULL) {
250 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n"));
251 ipsecstat.in_inval++;
252 goto fail;
254 #endif
258 * check for sequence number.
260 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
261 if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
262 ; /* okey */
263 else {
264 ipsecstat.in_ahreplay++;
265 ipseclog((LOG_WARNING,
266 "replay packet in IPv4 AH input: %s %s\n",
267 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
268 goto fail;
273 * alright, it seems sane. now we are going to check the
274 * cryptographic checksum.
276 cksum = kmalloc(siz1, M_TEMP, M_NOWAIT);
277 if (!cksum) {
278 ipseclog((LOG_DEBUG, "IPv4 AH input: "
279 "couldn't alloc temporary region for cksum\n"));
280 ipsecstat.in_inval++;
281 goto fail;
285 * some of IP header fields are flipped to the host endian.
286 * convert them back to network endian. VERY stupid.
288 ip->ip_len = htons(ip->ip_len + hlen);
289 ip->ip_off = htons(ip->ip_off);
290 if (ah4_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) {
291 kfree(cksum, M_TEMP);
292 ipsecstat.in_inval++;
293 goto fail;
295 ipsecstat.in_ahhist[sav->alg_auth]++;
297 * flip them back.
299 ip->ip_len = ntohs(ip->ip_len) - hlen;
300 ip->ip_off = ntohs(ip->ip_off);
303 caddr_t sumpos = NULL;
305 if (sav->flags & SADB_X_EXT_OLD) {
306 /* RFC 1826 */
307 sumpos = (caddr_t)(ah + 1);
308 } else {
309 /* RFC 2402 */
310 sumpos = (caddr_t)(((struct newah *)ah) + 1);
313 if (bcmp(sumpos, cksum, siz) != 0) {
314 ipseclog((LOG_WARNING,
315 "checksum mismatch in IPv4 AH input: %s %s\n",
316 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
317 kfree(cksum, M_TEMP);
318 ipsecstat.in_ahauthfail++;
319 goto fail;
323 kfree(cksum, M_TEMP);
325 m->m_flags |= M_AUTHIPHDR;
326 m->m_flags |= M_AUTHIPDGM;
328 #if 0
330 * looks okey, but we need more sanity check.
331 * XXX should elaborate.
333 if (ah->ah_nxt == IPPROTO_IPIP || ah->ah_nxt == IPPROTO_IP) {
334 struct ip *nip;
335 size_t sizoff;
337 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
339 if (m->m_len < off + sizeof(struct ah) + sizoff + siz1 + hlen) {
340 m = m_pullup(m, off + sizeof(struct ah)
341 + sizoff + siz1 + hlen);
342 if (!m) {
343 ipseclog((LOG_DEBUG,
344 "IPv4 AH input: can't pullup\n"));
345 ipsecstat.in_inval++;
346 goto fail;
350 nip = (struct ip *)((u_char *)(ah + 1) + sizoff + siz1);
351 if (nip->ip_src.s_addr != ip->ip_src.s_addr
352 || nip->ip_dst.s_addr != ip->ip_dst.s_addr) {
353 m->m_flags &= ~M_AUTHIPHDR;
354 m->m_flags &= ~M_AUTHIPDGM;
357 #ifdef INET6
358 else if (ah->ah_nxt == IPPROTO_IPV6) {
359 m->m_flags &= ~M_AUTHIPHDR;
360 m->m_flags &= ~M_AUTHIPDGM;
362 #endif /* INET6 */
363 #endif /* 0 */
365 if (m->m_flags & M_AUTHIPHDR
366 && m->m_flags & M_AUTHIPDGM) {
367 #if 0
368 ipseclog((LOG_DEBUG,
369 "IPv4 AH input: authentication succeess\n"));
370 #endif
371 ipsecstat.in_ahauthsucc++;
372 } else {
373 ipseclog((LOG_WARNING,
374 "authentication failed in IPv4 AH input: %s %s\n",
375 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
376 ipsecstat.in_ahauthfail++;
377 goto fail;
381 * update sequence number.
383 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
384 if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
385 ipsecstat.in_ahreplay++;
386 goto fail;
390 /* was it transmitted over the IPsec tunnel SA? */
391 if (sav->flags & SADB_X_EXT_OLD) {
392 /* RFC 1826 */
393 stripsiz = sizeof(struct ah) + siz1;
394 } else {
395 /* RFC 2402 */
396 stripsiz = sizeof(struct newah) + siz1;
398 if (ipsec4_tunnel_validate(m, off + stripsiz, nxt, sav)) {
400 * strip off all the headers that precedes AH.
401 * IP xx AH IP' payload -> IP' payload
403 * XXX more sanity checks
404 * XXX relationship with gif?
406 u_int8_t tos;
408 tos = ip->ip_tos;
409 m_adj(m, off + stripsiz);
410 if (m->m_len < sizeof(*ip)) {
411 m = m_pullup(m, sizeof(*ip));
412 if (!m) {
413 ipsecstat.in_inval++;
414 goto fail;
417 ip = mtod(m, struct ip *);
418 /* ECN consideration. */
419 ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos);
420 if (!key_checktunnelsanity(sav, AF_INET,
421 (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) {
422 ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
423 "in IPv4 AH input: %s %s\n",
424 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
425 ipsecstat.in_inval++;
426 goto fail;
429 #if 1
431 * Should the inner packet be considered authentic?
432 * My current answer is: NO.
434 * host1 -- gw1 === gw2 -- host2
435 * In this case, gw2 can trust the authenticity of the
436 * outer packet, but NOT inner. Packet may be altered
437 * between host1 and gw1.
439 * host1 -- gw1 === host2
440 * This case falls into the same scenario as above.
442 * host1 === host2
443 * This case is the only case when we may be able to leave
444 * M_AUTHIPHDR and M_AUTHIPDGM set.
445 * However, if host1 is wrongly configured, and allows
446 * attacker to inject some packet with src=host1 and
447 * dst=host2, you are in risk.
449 m->m_flags &= ~M_AUTHIPHDR;
450 m->m_flags &= ~M_AUTHIPDGM;
451 #endif
453 key_sa_recordxfer(sav, m);
454 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 ||
455 ipsec_addhist(m, IPPROTO_IPV4, 0) != 0) {
456 ipsecstat.in_nomem++;
457 goto fail;
460 if (netisr_queue(NETISR_IP, m)) {
461 ipsecstat.in_inval++;
462 m = NULL;
463 goto fail;
466 nxt = IPPROTO_DONE;
467 } else {
469 * strip off AH.
472 ip = mtod(m, struct ip *);
473 #ifndef PULLDOWN_TEST
475 * We do deep-copy since KAME requires that
476 * the packet is placed in a single external mbuf.
478 ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), off);
479 m->m_data += stripsiz;
480 m->m_len -= stripsiz;
481 m->m_pkthdr.len -= stripsiz;
482 #else
484 * even in m_pulldown case, we need to strip off AH so that
485 * we can compute checksum for multiple AH correctly.
487 if (m->m_len >= stripsiz + off) {
488 ovbcopy((caddr_t)ip, ((caddr_t)ip) + stripsiz, off);
489 m->m_data += stripsiz;
490 m->m_len -= stripsiz;
491 m->m_pkthdr.len -= stripsiz;
492 } else {
494 * this comes with no copy if the boundary is on
495 * cluster
497 struct mbuf *n;
499 n = m_split(m, off, MB_DONTWAIT);
500 if (n == NULL) {
501 /* m is retained by m_split */
502 goto fail;
504 m_adj(n, stripsiz);
505 /* m_cat does not update m_pkthdr.len */
506 m->m_pkthdr.len += n->m_pkthdr.len;
507 m_cat(m, n);
509 #endif
511 if (m->m_len < sizeof(*ip)) {
512 m = m_pullup(m, sizeof(*ip));
513 if (m == NULL) {
514 ipsecstat.in_inval++;
515 goto fail;
518 ip = mtod(m, struct ip *);
519 #ifdef IPLEN_FLIPPED
520 ip->ip_len = ip->ip_len - stripsiz;
521 #else
522 ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz);
523 #endif
524 ip->ip_p = nxt;
525 /* forget about IP hdr checksum, the check has already been passed */
527 key_sa_recordxfer(sav, m);
528 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
529 ipsecstat.in_nomem++;
530 goto fail;
533 if (nxt != IPPROTO_DONE) {
534 if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) &&
535 ipsec4_in_reject(m, NULL)) {
536 ipsecstat.in_polvio++;
537 goto fail;
539 if (!ip_lengthcheck(&m)) {
540 /* freed in ip_lengthcheck() */
541 goto fail;
543 (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt);
544 } else
545 m_freem(m);
546 m = NULL;
549 if (sav) {
550 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
551 kprintf("DP ah4_input call free SA:%p\n", sav));
552 key_freesav(sav);
554 ipsecstat.in_success++;
555 return;
557 fail:
558 if (sav) {
559 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
560 kprintf("DP ah4_input call free SA:%p\n", sav));
561 key_freesav(sav);
563 if (m)
564 m_freem(m);
565 return;
567 #endif /* INET */
569 #ifdef INET6
571 ah6_input(struct mbuf **mp, int *offp, int proto)
573 struct mbuf *m = *mp;
574 int off = *offp;
575 struct ip6_hdr *ip6;
576 struct ah *ah;
577 u_int32_t spi;
578 const struct ah_algorithm *algo;
579 size_t siz;
580 size_t siz1;
581 u_char *cksum;
582 struct secasvar *sav = NULL;
583 u_int16_t nxt;
584 size_t stripsiz = 0;
586 #ifndef PULLDOWN_TEST
587 IP6_EXTHDR_CHECK(m, off, sizeof(struct ah), IPPROTO_DONE);
588 ah = (struct ah *)(mtod(m, caddr_t) + off);
589 #else
590 IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
591 if (ah == NULL) {
592 ipseclog((LOG_DEBUG, "IPv6 AH input: can't pullup\n"));
593 ipsec6stat.in_inval++;
594 return IPPROTO_DONE;
596 #endif
597 ip6 = mtod(m, struct ip6_hdr *);
598 nxt = ah->ah_nxt;
600 /* find the sassoc. */
601 spi = ah->ah_spi;
603 if (ntohs(ip6->ip6_plen) == 0) {
604 ipseclog((LOG_ERR, "IPv6 AH input: "
605 "AH with IPv6 jumbogram is not supported.\n"));
606 ipsec6stat.in_inval++;
607 goto fail;
610 if ((sav = key_allocsa(AF_INET6,
611 (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst,
612 IPPROTO_AH, spi)) == 0) {
613 ipseclog((LOG_WARNING,
614 "IPv6 AH input: no key association found for spi %u\n",
615 (u_int32_t)ntohl(spi)));
616 ipsec6stat.in_nosa++;
617 goto fail;
619 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
620 kprintf("DP ah6_input called to allocate SA:%p\n", sav));
621 if (sav->state != SADB_SASTATE_MATURE
622 && sav->state != SADB_SASTATE_DYING) {
623 ipseclog((LOG_DEBUG,
624 "IPv6 AH input: non-mature/dying SA found for spi %u; ",
625 (u_int32_t)ntohl(spi)));
626 ipsec6stat.in_badspi++;
627 goto fail;
630 algo = ah_algorithm_lookup(sav->alg_auth);
631 if (!algo) {
632 ipseclog((LOG_DEBUG, "IPv6 AH input: "
633 "unsupported authentication algorithm for spi %u\n",
634 (u_int32_t)ntohl(spi)));
635 ipsec6stat.in_badspi++;
636 goto fail;
639 siz = (*algo->sumsiz)(sav);
640 siz1 = ((siz + 3) & ~(4 - 1));
643 * sanity checks for header, 1.
646 int sizoff;
648 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
651 * Here, we do not do "siz1 == siz". See ah4_input() for complete
652 * description.
654 if (siz1 < siz) {
655 ipseclog((LOG_NOTICE, "sum length too short in IPv6 AH input "
656 "(%lu, should be at least %lu): %s\n",
657 (u_long)siz1, (u_long)siz,
658 ipsec6_logpacketstr(ip6, spi)));
659 ipsec6stat.in_inval++;
660 goto fail;
662 if ((ah->ah_len << 2) - sizoff != siz1) {
663 ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input "
664 "(%d should be %lu): %s\n",
665 (ah->ah_len << 2) - sizoff, (u_long)siz1,
666 ipsec6_logpacketstr(ip6, spi)));
667 ipsec6stat.in_inval++;
668 goto fail;
670 #ifndef PULLDOWN_TEST
671 IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1, IPPROTO_DONE);
672 #else
673 IP6_EXTHDR_GET(ah, struct ah *, m, off,
674 sizeof(struct ah) + sizoff + siz1);
675 if (ah == NULL) {
676 ipseclog((LOG_NOTICE, "couldn't pullup gather IPv6 AH checksum part"));
677 ipsec6stat.in_inval++;
678 m = NULL;
679 goto fail;
681 #endif
685 * check for sequence number.
687 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
688 if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
689 ; /* okey */
690 else {
691 ipsec6stat.in_ahreplay++;
692 ipseclog((LOG_WARNING,
693 "replay packet in IPv6 AH input: %s %s\n",
694 ipsec6_logpacketstr(ip6, spi),
695 ipsec_logsastr(sav)));
696 goto fail;
701 * alright, it seems sane. now we are going to check the
702 * cryptographic checksum.
704 cksum = kmalloc(siz1, M_TEMP, M_NOWAIT);
705 if (!cksum) {
706 ipseclog((LOG_DEBUG, "IPv6 AH input: "
707 "couldn't alloc temporary region for cksum\n"));
708 ipsec6stat.in_inval++;
709 goto fail;
712 if (ah6_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) {
713 kfree(cksum, M_TEMP);
714 ipsec6stat.in_inval++;
715 goto fail;
717 ipsec6stat.in_ahhist[sav->alg_auth]++;
720 caddr_t sumpos = NULL;
722 if (sav->flags & SADB_X_EXT_OLD) {
723 /* RFC 1826 */
724 sumpos = (caddr_t)(ah + 1);
725 } else {
726 /* RFC 2402 */
727 sumpos = (caddr_t)(((struct newah *)ah) + 1);
730 if (bcmp(sumpos, cksum, siz) != 0) {
731 ipseclog((LOG_WARNING,
732 "checksum mismatch in IPv6 AH input: %s %s\n",
733 ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
734 kfree(cksum, M_TEMP);
735 ipsec6stat.in_ahauthfail++;
736 goto fail;
740 kfree(cksum, M_TEMP);
742 m->m_flags |= M_AUTHIPHDR;
743 m->m_flags |= M_AUTHIPDGM;
745 #if 0
747 * looks okey, but we need more sanity check.
748 * XXX should elaborate.
750 if (ah->ah_nxt == IPPROTO_IPV6) {
751 struct ip6_hdr *nip6;
752 size_t sizoff;
754 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
756 IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1
757 + sizeof(struct ip6_hdr), IPPROTO_DONE);
759 nip6 = (struct ip6_hdr *)((u_char *)(ah + 1) + sizoff + siz1);
760 if (!IN6_ARE_ADDR_EQUAL(&nip6->ip6_src, &ip6->ip6_src)
761 || !IN6_ARE_ADDR_EQUAL(&nip6->ip6_dst, &ip6->ip6_dst)) {
762 m->m_flags &= ~M_AUTHIPHDR;
763 m->m_flags &= ~M_AUTHIPDGM;
765 } else if (ah->ah_nxt == IPPROTO_IPIP) {
766 m->m_flags &= ~M_AUTHIPHDR;
767 m->m_flags &= ~M_AUTHIPDGM;
768 } else if (ah->ah_nxt == IPPROTO_IP) {
769 m->m_flags &= ~M_AUTHIPHDR;
770 m->m_flags &= ~M_AUTHIPDGM;
772 #endif
774 if (m->m_flags & M_AUTHIPHDR
775 && m->m_flags & M_AUTHIPDGM) {
776 #if 0
777 ipseclog((LOG_DEBUG,
778 "IPv6 AH input: authentication succeess\n"));
779 #endif
780 ipsec6stat.in_ahauthsucc++;
781 } else {
782 ipseclog((LOG_WARNING,
783 "authentication failed in IPv6 AH input: %s %s\n",
784 ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
785 ipsec6stat.in_ahauthfail++;
786 goto fail;
790 * update sequence number.
792 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
793 if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
794 ipsec6stat.in_ahreplay++;
795 goto fail;
799 /* was it transmitted over the IPsec tunnel SA? */
800 if (sav->flags & SADB_X_EXT_OLD) {
801 /* RFC 1826 */
802 stripsiz = sizeof(struct ah) + siz1;
803 } else {
804 /* RFC 2402 */
805 stripsiz = sizeof(struct newah) + siz1;
807 if (ipsec6_tunnel_validate(m, off + stripsiz, nxt, sav)) {
809 * strip off all the headers that precedes AH.
810 * IP6 xx AH IP6' payload -> IP6' payload
812 * XXX more sanity checks
813 * XXX relationship with gif?
815 u_int32_t flowinfo; /* net endian */
817 flowinfo = ip6->ip6_flow;
818 m_adj(m, off + stripsiz);
819 if (m->m_len < sizeof(*ip6)) {
821 * m_pullup is prohibited in KAME IPv6 input processing
822 * but there's no other way!
824 m = m_pullup(m, sizeof(*ip6));
825 if (!m) {
826 ipsec6stat.in_inval++;
827 goto fail;
830 ip6 = mtod(m, struct ip6_hdr *);
831 /* ECN consideration. */
832 ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow);
833 if (!key_checktunnelsanity(sav, AF_INET6,
834 (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) {
835 ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
836 "in IPv6 AH input: %s %s\n",
837 ipsec6_logpacketstr(ip6, spi),
838 ipsec_logsastr(sav)));
839 ipsec6stat.in_inval++;
840 goto fail;
843 #if 1
845 * should the inner packet be considered authentic?
846 * see comment in ah4_input().
848 m->m_flags &= ~M_AUTHIPHDR;
849 m->m_flags &= ~M_AUTHIPDGM;
850 #endif
852 key_sa_recordxfer(sav, m);
853 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 ||
854 ipsec_addhist(m, IPPROTO_IPV6, 0) != 0) {
855 ipsec6stat.in_nomem++;
856 goto fail;
859 if (netisr_queue(NETISR_IPV6, m)) {
860 ipsec6stat.in_inval++;
861 m = NULL;
862 goto fail;
865 nxt = IPPROTO_DONE;
866 } else {
868 * strip off AH.
870 char *prvnxtp;
873 * Copy the value of the next header field of AH to the
874 * next header field of the previous header.
875 * This is necessary because AH will be stripped off below.
877 prvnxtp = ip6_get_prevhdr(m, off); /* XXX */
878 *prvnxtp = nxt;
880 ip6 = mtod(m, struct ip6_hdr *);
881 #ifndef PULLDOWN_TEST
883 * We do deep-copy since KAME requires that
884 * the packet is placed in a single mbuf.
886 ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
887 m->m_data += stripsiz;
888 m->m_len -= stripsiz;
889 m->m_pkthdr.len -= stripsiz;
890 #else
892 * even in m_pulldown case, we need to strip off AH so that
893 * we can compute checksum for multiple AH correctly.
895 if (m->m_len >= stripsiz + off) {
896 ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
897 m->m_data += stripsiz;
898 m->m_len -= stripsiz;
899 m->m_pkthdr.len -= stripsiz;
900 } else {
902 * this comes with no copy if the boundary is on
903 * cluster
905 struct mbuf *n;
907 n = m_split(m, off, MB_DONTWAIT);
908 if (n == NULL) {
909 /* m is retained by m_split */
910 goto fail;
912 m_adj(n, stripsiz);
913 /* m_cat does not update m_pkthdr.len */
914 m->m_pkthdr.len += n->m_pkthdr.len;
915 m_cat(m, n);
917 #endif
918 ip6 = mtod(m, struct ip6_hdr *);
919 /* XXX jumbogram */
920 ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz);
922 key_sa_recordxfer(sav, m);
923 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
924 ipsec6stat.in_nomem++;
925 goto fail;
929 *offp = off;
930 *mp = m;
932 if (sav) {
933 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
934 kprintf("DP ah6_input call free SA:%p\n", sav));
935 key_freesav(sav);
937 ipsec6stat.in_success++;
938 return nxt;
940 fail:
941 if (sav) {
942 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
943 kprintf("DP ah6_input call free SA:%p\n", sav));
944 key_freesav(sav);
946 if (m)
947 m_freem(m);
948 return IPPROTO_DONE;
951 void
952 ah6_ctlinput(int cmd, struct sockaddr *sa, void *d)
954 const struct newah *ahp;
955 struct newah ah;
956 struct secasvar *sav;
957 struct ip6_hdr *ip6;
958 struct mbuf *m;
959 struct ip6ctlparam *ip6cp = NULL;
960 int off;
961 struct sockaddr_in6 *sa6_src, *sa6_dst;
963 if (sa->sa_family != AF_INET6 ||
964 sa->sa_len != sizeof(struct sockaddr_in6))
965 return;
966 if ((unsigned)cmd >= PRC_NCMDS)
967 return;
969 /* if the parameter is from icmp6, decode it. */
970 if (d != NULL) {
971 ip6cp = (struct ip6ctlparam *)d;
972 m = ip6cp->ip6c_m;
973 ip6 = ip6cp->ip6c_ip6;
974 off = ip6cp->ip6c_off;
975 } else {
976 m = NULL;
977 ip6 = NULL;
978 off = 0; /* fix warning */
981 if (ip6) {
983 * XXX: We assume that when ip6 is non NULL,
984 * M and OFF are valid.
987 /* check if we can safely examine src and dst ports */
988 if (m->m_pkthdr.len < off + sizeof(ah))
989 return;
991 if (m->m_len < off + sizeof(ah)) {
993 * this should be rare case,
994 * so we compromise on this copy...
996 m_copydata(m, off, sizeof(ah), (caddr_t)&ah);
997 ahp = &ah;
998 } else
999 ahp = (struct newah *)(mtod(m, caddr_t) + off);
1001 if (cmd == PRC_MSGSIZE) {
1002 int valid = 0;
1005 * Check to see if we have a valid SA corresponding to
1006 * the address in the ICMP message payload.
1008 sa6_src = ip6cp->ip6c_src;
1009 sa6_dst = (struct sockaddr_in6 *)sa;
1010 sav = key_allocsa(AF_INET6,
1011 (caddr_t)&sa6_src->sin6_addr,
1012 (caddr_t)&sa6_dst->sin6_addr,
1013 IPPROTO_AH, ahp->ah_spi);
1014 if (sav) {
1015 if (sav->state == SADB_SASTATE_MATURE ||
1016 sav->state == SADB_SASTATE_DYING)
1017 valid++;
1018 key_freesav(sav);
1021 /* XXX Further validation? */
1024 * Depending on the value of "valid" and routing table
1025 * size (mtudisc_{hi,lo}wat), we will:
1026 * - recalcurate the new MTU and create the
1027 * corresponding routing entry, or
1028 * - ignore the MTU change notification.
1030 icmp6_mtudisc_update((struct ip6ctlparam *)d, valid);
1033 /* we normally notify single pcb here */
1034 } else {
1035 /* we normally notify any pcb here */
1038 #endif /* INET6 */