No empty .Rs/.Re
[netbsd-mini2440.git] / sys / netinet6 / ah_input.c
blob652d249db6c788dac6ec300f9f42c77b83a2acbc
1 /* $NetBSD: ah_input.c,v 1.57 2008/04/24 11:38:38 ad Exp $ */
2 /* $KAME: ah_input.c,v 1.64 2001/09/04 08:43:19 itojun Exp $ */
4 /*
5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
34 * RFC1826/2402 authentication header.
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: ah_input.c,v 1.57 2008/04/24 11:38:38 ad Exp $");
40 #include "opt_inet.h"
41 #include "opt_ipsec.h"
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/malloc.h>
46 #include <sys/mbuf.h>
47 #include <sys/domain.h>
48 #include <sys/protosw.h>
49 #include <sys/socket.h>
50 #include <sys/errno.h>
51 #include <sys/time.h>
52 #include <sys/kernel.h>
53 #include <sys/syslog.h>
55 #include <net/if.h>
56 #include <net/route.h>
57 #include <net/netisr.h>
58 #include <sys/cpu.h>
60 #include <netinet/in.h>
61 #include <netinet/in_systm.h>
62 #include <netinet/in_var.h>
63 #include <netinet/in_proto.h>
64 #include <netinet/ip.h>
65 #include <netinet/ip_var.h>
66 #include <netinet/ip_ecn.h>
67 #include <netinet/ip_icmp.h>
69 #include <netinet/ip6.h>
71 #ifdef INET6
72 #include <netinet6/ip6_var.h>
73 #include <netinet/icmp6.h>
74 #include <netinet6/ip6protosw.h>
75 #endif
77 #include <netinet6/ipsec.h>
78 #include <netinet6/ipsec_private.h>
79 #include <netinet6/ah.h>
80 #include <netkey/key.h>
81 #include <netkey/keydb.h>
82 #include <netkey/key_debug.h>
84 #include <machine/stdarg.h>
86 #include <net/net_osdep.h>
88 /*#define IPLEN_FLIPPED*/
90 #ifdef INET
91 void
92 ah4_init(void)
95 ipsec4_init();
98 void
99 #if __STDC__
100 ah4_input(struct mbuf *m, ...)
101 #else
102 ah4_input(struct mbuf *m, va_alist)
103 struct mbuf *m;
104 va_dcl
105 #endif
107 struct ip *ip;
108 struct ah *ah;
109 u_int32_t spi;
110 const struct ah_algorithm *algo;
111 size_t siz;
112 size_t siz1;
113 u_int8_t cksum[AH_MAXSUMSIZE];
114 struct secasvar *sav = NULL;
115 u_int16_t nxt;
116 size_t hlen;
117 int s;
118 int off, proto;
119 va_list ap;
120 size_t stripsiz = 0;
121 u_int16_t sport = 0;
122 u_int16_t dport = 0;
123 #ifdef IPSEC_NAT_T
124 struct m_tag *tag = NULL;
125 #endif
127 va_start(ap, m);
128 off = va_arg(ap, int);
129 proto = va_arg(ap, int);
130 va_end(ap);
132 #ifdef IPSEC_NAT_T
133 /* find the source port for NAT-T */
134 if ((tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL)) != NULL) {
135 sport = ((u_int16_t *)(tag + 1))[0];
136 dport = ((u_int16_t *)(tag + 1))[1];
138 #endif
140 ip = mtod(m, struct ip *);
141 IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
142 if (ah == NULL) {
143 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;"
144 "dropping the packet for simplicity\n"));
145 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
146 goto fail;
148 nxt = ah->ah_nxt;
149 hlen = ip->ip_hl << 2;
151 /* find the sassoc. */
152 spi = ah->ah_spi;
154 if ((sav = key_allocsa(AF_INET,
155 (const void *)&ip->ip_src,
156 (const void *)&ip->ip_dst,
157 IPPROTO_AH, spi, sport, dport)) == 0) {
158 ipseclog((LOG_WARNING,
159 "IPv4 AH input: no key association found for spi %u\n",
160 (u_int32_t)ntohl(spi)));
161 IPSEC_STATINC(IPSEC_STAT_IN_NOSA);
162 goto fail;
164 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
165 printf("DP ah4_input called to allocate SA:%p\n", sav));
166 if (sav->state != SADB_SASTATE_MATURE &&
167 sav->state != SADB_SASTATE_DYING) {
168 ipseclog((LOG_DEBUG,
169 "IPv4 AH input: non-mature/dying SA found for spi %u\n",
170 (u_int32_t)ntohl(spi)));
171 IPSEC_STATINC(IPSEC_STAT_IN_BADSPI);
172 goto fail;
175 algo = ah_algorithm_lookup(sav->alg_auth);
176 if (!algo) {
177 ipseclog((LOG_DEBUG, "IPv4 AH input: "
178 "unsupported authentication algorithm for spi %u\n",
179 (u_int32_t)ntohl(spi)));
180 IPSEC_STATINC(IPSEC_STAT_IN_BADSPI);
181 goto fail;
184 siz = (*algo->sumsiz)(sav);
185 siz1 = ((siz + 3) & ~(4 - 1));
188 * sanity checks for header, 1.
191 int sizoff;
193 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
196 * Here, we do not do "siz1 == siz". This is because the way
197 * RFC240[34] section 2 is written. They do not require truncation
198 * to 96 bits.
199 * For example, Microsoft IPsec stack attaches 160 bits of
200 * authentication data for both hmac-md5 and hmac-sha1. For hmac-sha1,
201 * 32 bits of padding is attached.
203 * There are two downsides to this specification.
204 * They have no real harm, however, they leave us fuzzy feeling.
205 * - if we attach more than 96 bits of authentication data onto AH,
206 * we will never notice about possible modification by rogue
207 * intermediate nodes.
208 * Since extra bits in AH checksum is never used, this constitutes
209 * no real issue, however, it is wacky.
210 * - even if the peer attaches big authentication data, we will never
211 * notice the difference, since longer authentication data will just
212 * work.
214 * We may need some clarification in the spec.
216 if (siz1 < siz) {
217 ipseclog((LOG_NOTICE, "sum length too short in IPv4 AH input "
218 "(%lu, should be at least %lu): %s\n",
219 (u_long)siz1, (u_long)siz,
220 ipsec4_logpacketstr(ip, spi)));
221 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
222 goto fail;
224 if ((ah->ah_len << 2) - sizoff != siz1) {
225 ipseclog((LOG_NOTICE, "sum length mismatch in IPv4 AH input "
226 "(%d should be %lu): %s\n",
227 (ah->ah_len << 2) - sizoff, (u_long)siz1,
228 ipsec4_logpacketstr(ip, spi)));
229 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
230 goto fail;
232 if (siz1 > sizeof(cksum)) {
233 ipseclog((LOG_NOTICE, "sum length too large: %s\n",
234 ipsec4_logpacketstr(ip, spi)));
235 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
236 goto fail;
239 IP6_EXTHDR_GET(ah, struct ah *, m, off,
240 sizeof(struct ah) + sizoff + siz1);
241 if (ah == NULL) {
242 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n"));
243 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
244 goto fail;
249 * check for sequence number.
251 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
252 if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
253 ; /* okey */
254 else {
255 IPSEC_STATINC(IPSEC_STAT_IN_AHREPLAY);
256 ipseclog((LOG_WARNING,
257 "replay packet in IPv4 AH input: %s %s\n",
258 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
259 goto fail;
264 * alright, it seems sane. now we are going to check the
265 * cryptographic checksum.
269 #if 0
271 * some of IP header fields are flipped to the host endian.
272 * convert them back to network endian. VERY stupid.
274 ip->ip_len = htons(ip->ip_len);
275 ip->ip_off = htons(ip->ip_off);
276 #endif
277 if (ah4_calccksum(m, cksum, siz1, algo, sav)) {
278 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
279 goto fail;
281 IPSEC_STATINC(IPSEC_STAT_IN_AHHIST + sav->alg_auth);
282 #if 0
284 * flip them back.
286 ip->ip_len = ntohs(ip->ip_len);
287 ip->ip_off = ntohs(ip->ip_off);
288 #endif
292 void *sumpos = NULL;
294 if (sav->flags & SADB_X_EXT_OLD) {
295 /* RFC 1826 */
296 sumpos = (void *)(ah + 1);
297 } else {
298 /* RFC 2402 */
299 sumpos = (void *)(((struct newah *)ah) + 1);
302 if (memcmp(sumpos, cksum, siz) != 0) {
303 ipseclog((LOG_WARNING,
304 "checksum mismatch in IPv4 AH input: %s %s\n",
305 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
306 IPSEC_STATINC(IPSEC_STAT_IN_AHAUTHFAIL);
307 goto fail;
311 m->m_flags |= M_AUTHIPHDR;
312 m->m_flags |= M_AUTHIPDGM;
314 #if 0
316 * looks okey, but we need more sanity check.
317 * XXX should elaborate.
319 if (ah->ah_nxt == IPPROTO_IPIP || ah->ah_nxt == IPPROTO_IP) {
320 struct ip *nip;
321 size_t sizoff;
323 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
325 if (m->m_len < off + sizeof(struct ah) + sizoff + siz1 + hlen) {
326 m = m_pullup(m, off + sizeof(struct ah)
327 + sizoff + siz1 + hlen);
328 if (!m) {
329 ipseclog((LOG_DEBUG,
330 "IPv4 AH input: can't pullup\n"));
331 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
332 goto fail;
336 nip = (struct ip *)((u_char *)(ah + 1) + sizoff + siz1);
337 if (nip->ip_src.s_addr != ip->ip_src.s_addr
338 || nip->ip_dst.s_addr != ip->ip_dst.s_addr) {
339 m->m_flags &= ~M_AUTHIPHDR;
340 m->m_flags &= ~M_AUTHIPDGM;
343 #ifdef INET6
344 else if (ah->ah_nxt == IPPROTO_IPV6) {
345 m->m_flags &= ~M_AUTHIPHDR;
346 m->m_flags &= ~M_AUTHIPDGM;
348 #endif /* INET6 */
349 #endif /* 0 */
351 if (m->m_flags & M_AUTHIPHDR && m->m_flags & M_AUTHIPDGM) {
352 #if 0
353 ipseclog((LOG_DEBUG,
354 "IPv4 AH input: authentication succeess\n"));
355 #endif
356 IPSEC_STATINC(IPSEC_STAT_IN_AHAUTHSUCC);
357 } else {
358 ipseclog((LOG_WARNING,
359 "authentication failed in IPv4 AH input: %s %s\n",
360 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
361 IPSEC_STATINC(IPSEC_STAT_IN_AHAUTHFAIL);
362 goto fail;
366 * update sequence number.
368 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
369 if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
370 IPSEC_STATINC(IPSEC_STAT_IN_AHREPLAY);
371 goto fail;
375 /* was it transmitted over the IPsec tunnel SA? */
376 if (sav->flags & SADB_X_EXT_OLD) {
377 /* RFC 1826 */
378 stripsiz = sizeof(struct ah) + siz1;
379 } else {
380 /* RFC 2402 */
381 stripsiz = sizeof(struct newah) + siz1;
383 if (ipsec4_tunnel_validate(ip, nxt, sav)) {
385 * strip off all the headers that precedes AH.
386 * IP xx AH IP' payload -> IP' payload
388 * XXX more sanity checks
389 * XXX relationship with gif?
391 u_int8_t tos;
393 tos = ip->ip_tos;
394 m_adj(m, off + stripsiz);
395 if (m->m_len < sizeof(*ip)) {
396 m = m_pullup(m, sizeof(*ip));
397 if (!m) {
398 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
399 goto fail;
402 ip = mtod(m, struct ip *);
403 /* ECN consideration. */
404 ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos);
405 if (!key_checktunnelsanity(sav, AF_INET,
406 (void *)&ip->ip_src, (void *)&ip->ip_dst)) {
407 ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
408 "in IPv4 AH input: %s %s\n",
409 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
410 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
411 goto fail;
414 key_sa_recordxfer(sav, m);
415 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 ||
416 ipsec_addhist(m, IPPROTO_IPV4, 0) != 0) {
417 IPSEC_STATINC(IPSEC_STAT_IN_NOMEM);
418 goto fail;
421 s = splnet();
422 if (IF_QFULL(&ipintrq)) {
423 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
424 splx(s);
425 goto fail;
427 IF_ENQUEUE(&ipintrq, m);
428 m = NULL;
429 schednetisr(NETISR_IP); /* can be skipped but to make sure */
430 splx(s);
431 nxt = IPPROTO_DONE;
432 } else {
434 * strip off AH.
437 ip = mtod(m, struct ip *);
439 * even in m_pulldown case, we need to strip off AH so that
440 * we can compute checksum for multiple AH correctly.
442 if (m->m_len >= stripsiz + off) {
443 (void)memmove((char *)ip + stripsiz, ip, off);
444 m->m_data += stripsiz;
445 m->m_len -= stripsiz;
446 m->m_pkthdr.len -= stripsiz;
447 } else {
449 * this comes with no copy if the boundary is on
450 * cluster
452 struct mbuf *n;
454 n = m_split(m, off, M_DONTWAIT);
455 if (n == NULL) {
456 /* m is retained by m_split */
457 goto fail;
459 m_adj(n, stripsiz);
460 /* m_cat does not update m_pkthdr.len */
461 m->m_pkthdr.len += n->m_pkthdr.len;
462 m_cat(m, n);
465 if (m->m_len < sizeof(*ip)) {
466 m = m_pullup(m, sizeof(*ip));
467 if (m == NULL) {
468 IPSEC_STATINC(IPSEC_STAT_IN_INVAL);
469 goto fail;
472 ip = mtod(m, struct ip *);
473 #ifdef IPLEN_FLIPPED
474 ip->ip_len = ip->ip_len - stripsiz;
475 #else
476 ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz);
477 #endif
478 ip->ip_p = nxt;
479 /* forget about IP hdr checksum, the check has already been passed */
481 key_sa_recordxfer(sav, m);
482 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
483 IPSEC_STATINC(IPSEC_STAT_IN_NOMEM);
484 goto fail;
487 if (nxt != IPPROTO_DONE) {
488 if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 &&
489 ipsec4_in_reject(m, NULL)) {
490 IPSEC_STATINC(IPSEC_STAT_IN_POLVIO);
491 goto fail;
493 (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt);
494 } else
495 m_freem(m);
496 m = NULL;
499 if (sav) {
500 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
501 printf("DP ah4_input call free SA:%p\n", sav));
502 key_freesav(sav);
504 IPSEC_STATINC(IPSEC_STAT_IN_SUCCESS);
505 return;
507 fail:
508 if (sav) {
509 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
510 printf("DP ah4_input call free SA:%p\n", sav));
511 key_freesav(sav);
513 if (m)
514 m_freem(m);
515 return;
518 /* assumes that ip header and ah header are contiguous on mbuf */
519 void *
520 ah4_ctlinput(int cmd, const struct sockaddr *sa, void *v)
522 struct ip *ip = v;
523 struct ah *ah;
524 struct icmp *icp;
525 struct secasvar *sav;
527 if (sa->sa_family != AF_INET ||
528 sa->sa_len != sizeof(struct sockaddr_in))
529 return NULL;
530 if ((unsigned)cmd >= PRC_NCMDS)
531 return NULL;
532 if (cmd == PRC_MSGSIZE && ip_mtudisc && ip && ip->ip_v == 4) {
534 * Check to see if we have a valid SA corresponding to
535 * the address in the ICMP message payload.
537 ah = (struct ah *)((char *)ip + (ip->ip_hl << 2));
538 if ((sav = key_allocsa(AF_INET,
539 (void *) &ip->ip_src,
540 (void *) &ip->ip_dst,
541 IPPROTO_AH, ah->ah_spi, 0, 0)) == NULL)
542 return NULL;
543 if (sav->state != SADB_SASTATE_MATURE &&
544 sav->state != SADB_SASTATE_DYING) {
545 key_freesav(sav);
546 return NULL;
549 /* XXX Further validation? */
551 key_freesav(sav);
554 * Now that we've validated that we are actually communicating
555 * with the host indicated in the ICMP message, locate the
556 * ICMP header, recalculate the new MTU, and create the
557 * corresponding routing entry.
559 icp = (struct icmp *)((char *)ip -
560 offsetof(struct icmp, icmp_ip));
561 icmp_mtudisc(icp, ip->ip_dst);
563 return NULL;
566 return NULL;
568 #endif /* INET */
570 #ifdef INET6
571 void
572 ah6_init(void)
575 ipsec6_init();
579 ah6_input(struct mbuf **mp, int *offp, int proto)
581 struct mbuf *m = *mp;
582 int off = *offp;
583 struct ip6_hdr *ip6;
584 struct ah *ah;
585 u_int32_t spi;
586 const struct ah_algorithm *algo;
587 size_t siz;
588 size_t siz1;
589 u_int8_t cksum[AH_MAXSUMSIZE];
590 struct secasvar *sav = NULL;
591 u_int16_t nxt;
592 int s;
593 size_t stripsiz = 0;
595 IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
596 if (ah == NULL) {
597 ipseclog((LOG_DEBUG, "IPv6 AH input: can't pullup\n"));
598 IPSEC6_STATINC(IPSEC_STAT_IN_INVAL);
599 return IPPROTO_DONE;
601 ip6 = mtod(m, struct ip6_hdr *);
602 nxt = ah->ah_nxt;
604 /* find the sassoc. */
605 spi = ah->ah_spi;
607 if (ntohs(ip6->ip6_plen) == 0) {
608 ipseclog((LOG_ERR, "IPv6 AH input: "
609 "AH with IPv6 jumbogram is not supported.\n"));
610 IPSEC6_STATINC(IPSEC_STAT_IN_INVAL);
611 goto fail;
614 if ((sav = key_allocsa(AF_INET6,
615 (void *)&ip6->ip6_src, (void *)&ip6->ip6_dst,
616 IPPROTO_AH, spi, 0, 0)) == 0) {
617 ipseclog((LOG_WARNING,
618 "IPv6 AH input: no key association found for spi %u\n",
619 (u_int32_t)ntohl(spi)));
620 IPSEC6_STATINC(IPSEC_STAT_IN_NOSA);
621 goto fail;
623 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
624 printf("DP ah6_input called to allocate SA:%p\n", sav));
625 if (sav->state != SADB_SASTATE_MATURE &&
626 sav->state != SADB_SASTATE_DYING) {
627 ipseclog((LOG_DEBUG,
628 "IPv6 AH input: non-mature/dying SA found for spi %u; ",
629 (u_int32_t)ntohl(spi)));
630 IPSEC6_STATINC(IPSEC_STAT_IN_BADSPI);
631 goto fail;
634 algo = ah_algorithm_lookup(sav->alg_auth);
635 if (!algo) {
636 ipseclog((LOG_DEBUG, "IPv6 AH input: "
637 "unsupported authentication algorithm for spi %u\n",
638 (u_int32_t)ntohl(spi)));
639 IPSEC6_STATINC(IPSEC_STAT_IN_BADSPI);
640 goto fail;
643 siz = (*algo->sumsiz)(sav);
644 siz1 = ((siz + 3) & ~(4 - 1));
647 * sanity checks for header, 1.
650 int sizoff;
652 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
655 * Here, we do not do "siz1 == siz". See ah4_input() for complete
656 * description.
658 if (siz1 < siz) {
659 ipseclog((LOG_NOTICE, "sum length too short in IPv6 AH input "
660 "(%lu, should be at least %lu): %s\n",
661 (u_long)siz1, (u_long)siz,
662 ipsec6_logpacketstr(ip6, spi)));
663 IPSEC6_STATINC(IPSEC_STAT_IN_INVAL);
664 goto fail;
666 if ((ah->ah_len << 2) - sizoff != siz1) {
667 ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input "
668 "(%d should be %lu): %s\n",
669 (ah->ah_len << 2) - sizoff, (u_long)siz1,
670 ipsec6_logpacketstr(ip6, spi)));
671 IPSEC6_STATINC(IPSEC_STAT_IN_INVAL);
672 goto fail;
674 if (siz1 > sizeof(cksum)) {
675 ipseclog((LOG_NOTICE, "sum length too large: %s\n",
676 ipsec6_logpacketstr(ip6, spi)));
677 IPSEC6_STATINC(IPSEC_STAT_IN_INVAL);
678 goto fail;
681 IP6_EXTHDR_GET(ah, struct ah *, m, off,
682 sizeof(struct ah) + sizoff + siz1);
683 if (ah == NULL) {
684 ipseclog((LOG_NOTICE, "couldn't pullup gather IPv6 AH checksum part"));
685 IPSEC6_STATINC(IPSEC_STAT_IN_INVAL);
686 m = NULL;
687 goto fail;
692 * check for sequence number.
694 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
695 if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
696 ; /* okey */
697 else {
698 IPSEC6_STATINC(IPSEC_STAT_IN_AHREPLAY);
699 ipseclog((LOG_WARNING,
700 "replay packet in IPv6 AH input: %s %s\n",
701 ipsec6_logpacketstr(ip6, spi),
702 ipsec_logsastr(sav)));
703 goto fail;
708 * alright, it seems sane. now we are going to check the
709 * cryptographic checksum.
712 if (ah6_calccksum(m, cksum, siz1, algo, sav)) {
713 IPSEC6_STATINC(IPSEC_STAT_IN_INVAL);
714 goto fail;
716 IPSEC6_STATINC(IPSEC_STAT_IN_AHHIST + sav->alg_auth);
719 void *sumpos = NULL;
721 if (sav->flags & SADB_X_EXT_OLD) {
722 /* RFC 1826 */
723 sumpos = (void *)(ah + 1);
724 } else {
725 /* RFC 2402 */
726 sumpos = (void *)(((struct newah *)ah) + 1);
729 if (memcmp(sumpos, cksum, siz) != 0) {
730 ipseclog((LOG_WARNING,
731 "checksum mismatch in IPv6 AH input: %s %s\n",
732 ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
733 IPSEC6_STATINC(IPSEC_STAT_IN_AHAUTHFAIL);
734 goto fail;
738 m->m_flags |= M_AUTHIPHDR;
739 m->m_flags |= M_AUTHIPDGM;
741 #if 0
743 * looks okey, but we need more sanity check.
744 * XXX should elaborate.
746 if (ah->ah_nxt == IPPROTO_IPV6) {
747 struct ip6_hdr *nip6;
748 size_t sizoff;
750 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
752 IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1
753 + sizeof(struct ip6_hdr), IPPROTO_DONE);
755 nip6 = (struct ip6_hdr *)((u_char *)(ah + 1) + sizoff + siz1);
756 if (!IN6_ARE_ADDR_EQUAL(&nip6->ip6_src, &ip6->ip6_src)
757 || !IN6_ARE_ADDR_EQUAL(&nip6->ip6_dst, &ip6->ip6_dst)) {
758 m->m_flags &= ~M_AUTHIPHDR;
759 m->m_flags &= ~M_AUTHIPDGM;
761 } else if (ah->ah_nxt == IPPROTO_IPIP) {
762 m->m_flags &= ~M_AUTHIPHDR;
763 m->m_flags &= ~M_AUTHIPDGM;
764 } else if (ah->ah_nxt == IPPROTO_IP) {
765 m->m_flags &= ~M_AUTHIPHDR;
766 m->m_flags &= ~M_AUTHIPDGM;
768 #endif
770 if (m->m_flags & M_AUTHIPHDR && m->m_flags & M_AUTHIPDGM) {
771 #if 0
772 ipseclog((LOG_DEBUG,
773 "IPv6 AH input: authentication succeess\n"));
774 #endif
775 IPSEC6_STATINC(IPSEC_STAT_IN_AHAUTHSUCC);
776 } else {
777 ipseclog((LOG_WARNING,
778 "authentication failed in IPv6 AH input: %s %s\n",
779 ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
780 IPSEC6_STATINC(IPSEC_STAT_IN_AHAUTHFAIL);
781 goto fail;
785 * update sequence number.
787 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
788 if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
789 IPSEC6_STATINC(IPSEC_STAT_IN_AHREPLAY);
790 goto fail;
794 /* was it transmitted over the IPsec tunnel SA? */
795 if (sav->flags & SADB_X_EXT_OLD) {
796 /* RFC 1826 */
797 stripsiz = sizeof(struct ah) + siz1;
798 } else {
799 /* RFC 2402 */
800 stripsiz = sizeof(struct newah) + siz1;
802 if (ipsec6_tunnel_validate(ip6, nxt, sav)) {
804 * strip off all the headers that precedes AH.
805 * IP6 xx AH IP6' payload -> IP6' payload
807 * XXX more sanity checks
808 * XXX relationship with gif?
810 u_int32_t flowinfo; /* net endian */
812 flowinfo = ip6->ip6_flow;
813 m_adj(m, off + stripsiz);
814 if (m->m_len < sizeof(*ip6)) {
815 m = m_pullup(m, sizeof(*ip6));
816 if (!m) {
817 IPSEC6_STATINC(IPSEC_STAT_IN_INVAL);
818 goto fail;
821 ip6 = mtod(m, struct ip6_hdr *);
822 /* ECN consideration. */
823 ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow);
824 if (!key_checktunnelsanity(sav, AF_INET6,
825 (void *)&ip6->ip6_src, (void *)&ip6->ip6_dst)) {
826 ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
827 "in IPv6 AH input: %s %s\n",
828 ipsec6_logpacketstr(ip6, spi),
829 ipsec_logsastr(sav)));
830 IPSEC6_STATINC(IPSEC_STAT_IN_INVAL);
831 goto fail;
834 key_sa_recordxfer(sav, m);
835 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 ||
836 ipsec_addhist(m, IPPROTO_IPV6, 0) != 0) {
837 IPSEC6_STATINC(IPSEC_STAT_IN_NOMEM);
838 goto fail;
841 s = splnet();
842 if (IF_QFULL(&ip6intrq)) {
843 IPSEC6_STATINC(IPSEC_STAT_IN_INVAL);
844 splx(s);
845 goto fail;
847 IF_ENQUEUE(&ip6intrq, m);
848 m = NULL;
849 schednetisr(NETISR_IPV6); /* can be skipped but to make sure */
850 splx(s);
851 nxt = IPPROTO_DONE;
852 } else {
854 * strip off AH.
856 u_int8_t *prvnxtp;
859 * Copy the value of the next header field of AH to the
860 * next header field of the previous header.
861 * This is necessary because AH will be stripped off below.
863 prvnxtp = ip6_get_prevhdr(m, off); /* XXX */
864 *prvnxtp = nxt;
866 ip6 = mtod(m, struct ip6_hdr *);
868 * even in m_pulldown case, we need to strip off AH so that
869 * we can compute checksum for multiple AH correctly.
871 if (m->m_len >= stripsiz + off) {
872 (void)memmove((char *)ip6 + stripsiz, ip6, off);
873 m->m_data += stripsiz;
874 m->m_len -= stripsiz;
875 m->m_pkthdr.len -= stripsiz;
876 } else {
878 * this comes with no copy if the boundary is on
879 * cluster
881 struct mbuf *n;
883 n = m_split(m, off, M_DONTWAIT);
884 if (n == NULL) {
885 /* m is retained by m_split */
886 goto fail;
888 m_adj(n, stripsiz);
889 /* m_cat does not update m_pkthdr.len */
890 m->m_pkthdr.len += n->m_pkthdr.len;
891 m_cat(m, n);
893 ip6 = mtod(m, struct ip6_hdr *);
894 /* XXX jumbogram */
895 ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz);
897 key_sa_recordxfer(sav, m);
898 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
899 IPSEC6_STATINC(IPSEC_STAT_IN_NOMEM);
900 goto fail;
904 *offp = off;
905 *mp = m;
907 if (sav) {
908 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
909 printf("DP ah6_input call free SA:%p\n", sav));
910 key_freesav(sav);
912 IPSEC6_STATINC(IPSEC_STAT_IN_SUCCESS);
913 return nxt;
915 fail:
916 if (sav) {
917 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
918 printf("DP ah6_input call free SA:%p\n", sav));
919 key_freesav(sav);
921 if (m)
922 m_freem(m);
923 return IPPROTO_DONE;
926 void *
927 ah6_ctlinput(int cmd, const struct sockaddr *sa, void *d)
929 const struct newah *ahp;
930 struct newah ah;
931 struct secasvar *sav;
932 struct ip6_hdr *ip6;
933 struct mbuf *m;
934 struct ip6ctlparam *ip6cp = NULL;
935 int off;
936 const struct sockaddr_in6 *sa6_src, *sa6_dst;
938 if (sa->sa_family != AF_INET6 ||
939 sa->sa_len != sizeof(struct sockaddr_in6))
940 return NULL;
941 if ((unsigned)cmd >= PRC_NCMDS)
942 return NULL;
944 /* if the parameter is from icmp6, decode it. */
945 if (d != NULL) {
946 ip6cp = (struct ip6ctlparam *)d;
947 m = ip6cp->ip6c_m;
948 ip6 = ip6cp->ip6c_ip6;
949 off = ip6cp->ip6c_off;
950 } else {
951 m = NULL;
952 ip6 = NULL;
953 off = 0;
956 if (ip6) {
958 * XXX: We assume that when ip6 is non NULL,
959 * M and OFF are valid.
962 /* check if we can safely examine src and dst ports */
963 if (m->m_pkthdr.len < off + sizeof(ah))
964 return NULL;
966 if (m->m_len < off + sizeof(ah)) {
968 * this should be rare case,
969 * so we compromise on this copy...
971 m_copydata(m, off, sizeof(ah), &ah);
972 ahp = &ah;
973 } else
974 ahp = (struct newah *)(mtod(m, char *) + off);
976 if (cmd == PRC_MSGSIZE) {
977 int valid = 0;
980 * Check to see if we have a valid SA corresponding to
981 * the address in the ICMP message payload.
983 sa6_src = ip6cp->ip6c_src;
984 sa6_dst = (const struct sockaddr_in6 *)sa;
985 sav = key_allocsa(AF_INET6,
986 (const void *)&sa6_src->sin6_addr,
987 (const void *)&sa6_dst->sin6_addr,
988 IPPROTO_AH, ahp->ah_spi, 0, 0);
989 if (sav) {
990 if (sav->state == SADB_SASTATE_MATURE ||
991 sav->state == SADB_SASTATE_DYING)
992 valid++;
993 key_freesav(sav);
996 /* XXX Further validation? */
999 * Depending on the value of "valid" and routing table
1000 * size (mtudisc_{hi,lo}wat), we will:
1001 * - recalcurate the new MTU and create the
1002 * corresponding routing entry, or
1003 * - ignore the MTU change notification.
1005 icmp6_mtudisc_update((struct ip6ctlparam *)d, valid);
1008 /* we normally notify single pcb here */
1009 } else {
1010 /* we normally notify any pcb here */
1013 return NULL;
1015 #endif /* INET6 */