Remove struct ipprotosw. It's identical to protosw, so use the generic
[dragonfly.git] / sys / netinet6 / ah_input.c
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
99 extern struct protosw inetsw[];
100
101 void
102 ah4_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
557 fail:
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
570 int
571 ah6_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
940 fail:
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
951 void
952 ah6_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 */