Remove struct ipprotosw. It's identical to protosw, so use the generic
[dragonfly.git] / sys / netinet6 / ah_input.c
... / ...
CommitLineData
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.10 2004/11/30 19:21:26 joerg Exp $ */
3/* $KAME: ah_input.c,v 1.67 2002/01/07 11:39:56 kjc Exp $ */
4
5/*
6 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7 * All rights reserved.
8 *
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.
20 *
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.
32 */
33
34/*
35 * RFC1826/2402 authentication header.
36 */
37
38#include "opt_inet.h"
39#include "opt_inet6.h"
40
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>
51
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>
57
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
67
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
75
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
91
92#include <machine/stdarg.h>
93
94#include <net/net_osdep.h>
95
96#define IPLEN_FLIPPED
97
98#ifdef INET
99extern struct protosw inetsw[];
100
101void
102ah4_input(struct mbuf *m, ...)
103{
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;
117
118 __va_start(ap, m);
119 off = __va_arg(ap, int);
120 proto = __va_arg(ap, int);
121 __va_end(ap);
122
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;
131 }
132 }
133
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;
144 }
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
152
153 /* find the sassoc. */
154 spi = ah->ah_spi;
155
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;
164 }
165 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
166 printf("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;
174 }
175
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;
183 }
184
185 siz = (*algo->sumsiz)(sav);
186 siz1 = ((siz + 3) & ~(4 - 1));
187
188 /*
189 * sanity checks for header, 1.
190 */
191 {
192 int sizoff;
193
194 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
195
196 /*
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.
203 *
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.
214 *
215 * We may need some clarification in the spec.
216 */
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;
224 }
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;
232 }
233
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;
241 }
242
243 ip = mtod(m, struct ip *);
244 ah = (struct ah *)(((caddr_t)ip) + off);
245 }
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;
253 }
254#endif
255 }
256
257 /*
258 * check for sequence number.
259 */
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;
269 }
270 }
271
272 /*
273 * alright, it seems sane. now we are going to check the
274 * cryptographic checksum.
275 */
276 cksum = malloc(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;
282 }
283
284 /*
285 * some of IP header fields are flipped to the host endian.
286 * convert them back to network endian. VERY stupid.
287 */
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 free(cksum, M_TEMP);
292 ipsecstat.in_inval++;
293 goto fail;
294 }
295 ipsecstat.in_ahhist[sav->alg_auth]++;
296 /*
297 * flip them back.
298 */
299 ip->ip_len = ntohs(ip->ip_len) - hlen;
300 ip->ip_off = ntohs(ip->ip_off);
301
302 {
303 caddr_t sumpos = NULL;
304
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);
311 }
312
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 free(cksum, M_TEMP);
318 ipsecstat.in_ahauthfail++;
319 goto fail;
320 }
321 }
322
323 free(cksum, M_TEMP);
324
325 m->m_flags |= M_AUTHIPHDR;
326 m->m_flags |= M_AUTHIPDGM;
327
328#if 0
329 /*
330 * looks okey, but we need more sanity check.
331 * XXX should elaborate.
332 */
333 if (ah->ah_nxt == IPPROTO_IPIP || ah->ah_nxt == IPPROTO_IP) {
334 struct ip *nip;
335 size_t sizoff;
336
337 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
338
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;
347 }
348 }
349
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;
355 }
356 }
357#ifdef INET6
358 else if (ah->ah_nxt == IPPROTO_IPV6) {
359 m->m_flags &= ~M_AUTHIPHDR;
360 m->m_flags &= ~M_AUTHIPDGM;
361 }
362#endif /* INET6 */
363#endif /* 0 */
364
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;
378 }
379
380 /*
381 * update sequence number.
382 */
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;
387 }
388 }
389
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;
397 }
398 if (ipsec4_tunnel_validate(m, off + stripsiz, nxt, sav)) {
399 /*
400 * strip off all the headers that precedes AH.
401 * IP xx AH IP' payload -> IP' payload
402 *
403 * XXX more sanity checks
404 * XXX relationship with gif?
405 */
406 u_int8_t tos;
407
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;
415 }
416 }
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;
427 }
428
429#if 1
430 /*
431 * Should the inner packet be considered authentic?
432 * My current answer is: NO.
433 *
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.
438 *
439 * host1 -- gw1 === host2
440 * This case falls into the same scenario as above.
441 *
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.
448 */
449 m->m_flags &= ~M_AUTHIPHDR;
450 m->m_flags &= ~M_AUTHIPDGM;
451#endif
452
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;
458 }
459
460 if (netisr_queue(NETISR_IP, m)) {
461 ipsecstat.in_inval++;
462 m = NULL;
463 goto fail;
464 }
465
466 nxt = IPPROTO_DONE;
467 } else {
468 /*
469 * strip off AH.
470 */
471
472 ip = mtod(m, struct ip *);
473#ifndef PULLDOWN_TEST
474 /*
475 * We do deep-copy since KAME requires that
476 * the packet is placed in a single external mbuf.
477 */
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
483 /*
484 * even in m_pulldown case, we need to strip off AH so that
485 * we can compute checksum for multiple AH correctly.
486 */
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 {
493 /*
494 * this comes with no copy if the boundary is on
495 * cluster
496 */
497 struct mbuf *n;
498
499 n = m_split(m, off, MB_DONTWAIT);
500 if (n == NULL) {
501 /* m is retained by m_split */
502 goto fail;
503 }
504 m_adj(n, stripsiz);
505 m_cat(m, n);
506 /* m_cat does not update m_pkthdr.len */
507 m->m_pkthdr.len += n->m_pkthdr.len;
508 }
509#endif
510
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;
516 }
517 }
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 */
526
527 key_sa_recordxfer(sav, m);
528 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
529 ipsecstat.in_nomem++;
530 goto fail;
531 }
532
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;
538 }
539 if (!ip_lengthcheck(&m)) {
540 m = NULL; /* freed in ip_lengthcheck() */
541 goto fail;
542 }
543 (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt);
544 } else
545 m_freem(m);
546 m = NULL;
547 }
548
549 if (sav) {
550 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
551 printf("DP ah4_input call free SA:%p\n", sav));
552 key_freesav(sav);
553 }
554 ipsecstat.in_success++;
555 return;
556
557fail:
558 if (sav) {
559 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
560 printf("DP ah4_input call free SA:%p\n", sav));
561 key_freesav(sav);
562 }
563 if (m)
564 m_freem(m);
565 return;
566}
567#endif /* INET */
568
569#ifdef INET6
570int
571ah6_input(struct mbuf **mp, int *offp, int proto)
572{
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;
585
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;
595 }
596#endif
597 ip6 = mtod(m, struct ip6_hdr *);
598 nxt = ah->ah_nxt;
599
600 /* find the sassoc. */
601 spi = ah->ah_spi;
602
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;
608 }
609
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;
618 }
619 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
620 printf("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;
628 }
629
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;
637 }
638
639 siz = (*algo->sumsiz)(sav);
640 siz1 = ((siz + 3) & ~(4 - 1));
641
642 /*
643 * sanity checks for header, 1.
644 */
645 {
646 int sizoff;
647
648 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
649
650 /*
651 * Here, we do not do "siz1 == siz". See ah4_input() for complete
652 * description.
653 */
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;
661 }
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;
669 }
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;
680 }
681#endif
682 }
683
684 /*
685 * check for sequence number.
686 */
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;
697 }
698 }
699
700 /*
701 * alright, it seems sane. now we are going to check the
702 * cryptographic checksum.
703 */
704 cksum = malloc(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;
710 }
711
712 if (ah6_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) {
713 free(cksum, M_TEMP);
714 ipsec6stat.in_inval++;
715 goto fail;
716 }
717 ipsec6stat.in_ahhist[sav->alg_auth]++;
718
719 {
720 caddr_t sumpos = NULL;
721
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);
728 }
729
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 free(cksum, M_TEMP);
735 ipsec6stat.in_ahauthfail++;
736 goto fail;
737 }
738 }
739
740 free(cksum, M_TEMP);
741
742 m->m_flags |= M_AUTHIPHDR;
743 m->m_flags |= M_AUTHIPDGM;
744
745#if 0
746 /*
747 * looks okey, but we need more sanity check.
748 * XXX should elaborate.
749 */
750 if (ah->ah_nxt == IPPROTO_IPV6) {
751 struct ip6_hdr *nip6;
752 size_t sizoff;
753
754 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
755
756 IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1
757 + sizeof(struct ip6_hdr), IPPROTO_DONE);
758
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;
764 }
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;
771 }
772#endif
773
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;
787 }
788
789 /*
790 * update sequence number.
791 */
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;
796 }
797 }
798
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;
806 }
807 if (ipsec6_tunnel_validate(m, off + stripsiz, nxt, sav)) {
808 /*
809 * strip off all the headers that precedes AH.
810 * IP6 xx AH IP6' payload -> IP6' payload
811 *
812 * XXX more sanity checks
813 * XXX relationship with gif?
814 */
815 u_int32_t flowinfo; /* net endian */
816
817 flowinfo = ip6->ip6_flow;
818 m_adj(m, off + stripsiz);
819 if (m->m_len < sizeof(*ip6)) {
820 /*
821 * m_pullup is prohibited in KAME IPv6 input processing
822 * but there's no other way!
823 */
824 m = m_pullup(m, sizeof(*ip6));
825 if (!m) {
826 ipsec6stat.in_inval++;
827 goto fail;
828 }
829 }
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;
841 }
842
843#if 1
844 /*
845 * should the inner packet be considered authentic?
846 * see comment in ah4_input().
847 */
848 m->m_flags &= ~M_AUTHIPHDR;
849 m->m_flags &= ~M_AUTHIPDGM;
850#endif
851
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;
857 }
858
859 if (netisr_queue(NETISR_IPV6, m)) {
860 ipsecstat.in_inval++;
861 m = NULL;
862 goto fail;
863 }
864
865 nxt = IPPROTO_DONE;
866 } else {
867 /*
868 * strip off AH.
869 */
870 char *prvnxtp;
871
872 /*
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.
876 */
877 prvnxtp = ip6_get_prevhdr(m, off); /* XXX */
878 *prvnxtp = nxt;
879
880 ip6 = mtod(m, struct ip6_hdr *);
881#ifndef PULLDOWN_TEST
882 /*
883 * We do deep-copy since KAME requires that
884 * the packet is placed in a single mbuf.
885 */
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
891 /*
892 * even in m_pulldown case, we need to strip off AH so that
893 * we can compute checksum for multiple AH correctly.
894 */
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 {
901 /*
902 * this comes with no copy if the boundary is on
903 * cluster
904 */
905 struct mbuf *n;
906
907 n = m_split(m, off, MB_DONTWAIT);
908 if (n == NULL) {
909 /* m is retained by m_split */
910 goto fail;
911 }
912 m_adj(n, stripsiz);
913 m_cat(m, n);
914 /* m_cat does not update m_pkthdr.len */
915 m->m_pkthdr.len += n->m_pkthdr.len;
916 }
917#endif
918 ip6 = mtod(m, struct ip6_hdr *);
919 /* XXX jumbogram */
920 ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz);
921
922 key_sa_recordxfer(sav, m);
923 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
924 ipsec6stat.in_nomem++;
925 goto fail;
926 }
927 }
928
929 *offp = off;
930 *mp = m;
931
932 if (sav) {
933 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
934 printf("DP ah6_input call free SA:%p\n", sav));
935 key_freesav(sav);
936 }
937 ipsec6stat.in_success++;
938 return nxt;
939
940fail:
941 if (sav) {
942 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
943 printf("DP ah6_input call free SA:%p\n", sav));
944 key_freesav(sav);
945 }
946 if (m)
947 m_freem(m);
948 return IPPROTO_DONE;
949}
950
951void
952ah6_ctlinput(int cmd, struct sockaddr *sa, void *d)
953{
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;
962
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;
968
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 */
979 }
980
981 if (ip6) {
982 /*
983 * XXX: We assume that when ip6 is non NULL,
984 * M and OFF are valid.
985 */
986
987 /* check if we can safely examine src and dst ports */
988 if (m->m_pkthdr.len < off + sizeof(ah))
989 return;
990
991 if (m->m_len < off + sizeof(ah)) {
992 /*
993 * this should be rare case,
994 * so we compromise on this copy...
995 */
996 m_copydata(m, off, sizeof(ah), (caddr_t)&ah);
997 ahp = &ah;
998 } else
999 ahp = (struct newah *)(mtod(m, caddr_t) + off);
1000
1001 if (cmd == PRC_MSGSIZE) {
1002 int valid = 0;
1003
1004 /*
1005 * Check to see if we have a valid SA corresponding to
1006 * the address in the ICMP message payload.
1007 */
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);
1019 }
1020
1021 /* XXX Further validation? */
1022
1023 /*
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.
1029 */
1030 icmp6_mtudisc_update((struct ip6ctlparam *)d, valid);
1031 }
1032
1033 /* we normally notify single pcb here */
1034 } else {
1035 /* we normally notify any pcb here */
1036 }
1037}
1038#endif /* INET6 */