The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netinet6/ah_input.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: ah_input.c,v 1.44 2004/02/11 10:47:28 itojun Exp $     */
    2 /*      $KAME: ah_input.c,v 1.64 2001/09/04 08:43:19 itojun Exp $       */
    3 
    4 /*
    5  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
    6  * All rights reserved.
    7  *
    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.
   19  *
   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.
   31  */
   32 
   33 /*
   34  * RFC1826/2402 authentication header.
   35  */
   36 
   37 #include <sys/cdefs.h>
   38 __KERNEL_RCSID(0, "$NetBSD: ah_input.c,v 1.44 2004/02/11 10:47:28 itojun Exp $");
   39 
   40 #include "opt_inet.h"
   41 
   42 #include <sys/param.h>
   43 #include <sys/systm.h>
   44 #include <sys/malloc.h>
   45 #include <sys/mbuf.h>
   46 #include <sys/domain.h>
   47 #include <sys/protosw.h>
   48 #include <sys/socket.h>
   49 #include <sys/errno.h>
   50 #include <sys/time.h>
   51 #include <sys/kernel.h>
   52 #include <sys/syslog.h>
   53 
   54 #include <net/if.h>
   55 #include <net/route.h>
   56 #include <net/netisr.h>
   57 #include <machine/cpu.h>
   58 
   59 #include <netinet/in.h>
   60 #include <netinet/in_systm.h>
   61 #include <netinet/in_var.h>
   62 #include <netinet/ip.h>
   63 #include <netinet/ip_var.h>
   64 #include <netinet/ip_ecn.h>
   65 #include <netinet/ip_icmp.h>
   66 
   67 #include <netinet/ip6.h>
   68 
   69 #ifdef INET6
   70 #include <netinet6/ip6_var.h>
   71 #include <netinet/icmp6.h>
   72 #include <netinet6/ip6protosw.h>
   73 #endif
   74 
   75 #include <netinet6/ipsec.h>
   76 #include <netinet6/ah.h>
   77 #include <netkey/key.h>
   78 #include <netkey/keydb.h>
   79 #include <netkey/key_debug.h>
   80 
   81 #include <machine/stdarg.h>
   82 
   83 #include <net/net_osdep.h>
   84 
   85 /*#define IPLEN_FLIPPED*/
   86 
   87 #ifdef INET
   88 void
   89 #if __STDC__
   90 ah4_input(struct mbuf *m, ...)
   91 #else
   92 ah4_input(m, va_alist)
   93         struct mbuf *m;
   94         va_dcl
   95 #endif
   96 {
   97         struct ip *ip;
   98         struct ah *ah;
   99         u_int32_t spi;
  100         const struct ah_algorithm *algo;
  101         size_t siz;
  102         size_t siz1;
  103         u_int8_t cksum[AH_MAXSUMSIZE];
  104         struct secasvar *sav = NULL;
  105         u_int16_t nxt;
  106         size_t hlen;
  107         int s;
  108         int off, proto;
  109         va_list ap;
  110         size_t stripsiz = 0;
  111 
  112         va_start(ap, m);
  113         off = va_arg(ap, int);
  114         proto = va_arg(ap, int);
  115         va_end(ap);
  116 
  117         ip = mtod(m, struct ip *);
  118         IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
  119         if (ah == NULL) {
  120                 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;"
  121                         "dropping the packet for simplicity\n"));
  122                 ipsecstat.in_inval++;
  123                 goto fail;
  124         }
  125         nxt = ah->ah_nxt;
  126         hlen = ip->ip_hl << 2;
  127 
  128         /* find the sassoc. */
  129         spi = ah->ah_spi;
  130 
  131         if ((sav = key_allocsa(AF_INET,
  132                               (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
  133                               IPPROTO_AH, spi)) == 0) {
  134                 ipseclog((LOG_WARNING,
  135                     "IPv4 AH input: no key association found for spi %u\n",
  136                     (u_int32_t)ntohl(spi)));
  137                 ipsecstat.in_nosa++;
  138                 goto fail;
  139         }
  140         KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
  141                 printf("DP ah4_input called to allocate SA:%p\n", sav));
  142         if (sav->state != SADB_SASTATE_MATURE &&
  143             sav->state != SADB_SASTATE_DYING) {
  144                 ipseclog((LOG_DEBUG,
  145                     "IPv4 AH input: non-mature/dying SA found for spi %u\n",
  146                     (u_int32_t)ntohl(spi)));
  147                 ipsecstat.in_badspi++;
  148                 goto fail;
  149         }
  150 
  151         algo = ah_algorithm_lookup(sav->alg_auth);
  152         if (!algo) {
  153                 ipseclog((LOG_DEBUG, "IPv4 AH input: "
  154                     "unsupported authentication algorithm for spi %u\n",
  155                     (u_int32_t)ntohl(spi)));
  156                 ipsecstat.in_badspi++;
  157                 goto fail;
  158         }
  159 
  160         siz = (*algo->sumsiz)(sav);
  161         siz1 = ((siz + 3) & ~(4 - 1));
  162 
  163         /*
  164          * sanity checks for header, 1.
  165          */
  166     {
  167         int sizoff;
  168 
  169         sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
  170 
  171         /*
  172          * Here, we do not do "siz1 == siz".  This is because the way
  173          * RFC240[34] section 2 is written.  They do not require truncation
  174          * to 96 bits.
  175          * For example, Microsoft IPsec stack attaches 160 bits of
  176          * authentication data for both hmac-md5 and hmac-sha1.  For hmac-sha1,
  177          * 32 bits of padding is attached.
  178          *
  179          * There are two downsides to this specification.
  180          * They have no real harm, however, they leave us fuzzy feeling.
  181          * - if we attach more than 96 bits of authentication data onto AH,
  182          *   we will never notice about possible modification by rogue
  183          *   intermediate nodes.
  184          *   Since extra bits in AH checksum is never used, this constitutes
  185          *   no real issue, however, it is wacky.
  186          * - even if the peer attaches big authentication data, we will never
  187          *   notice the difference, since longer authentication data will just
  188          *   work.
  189          *
  190          * We may need some clarification in the spec.
  191          */
  192         if (siz1 < siz) {
  193                 ipseclog((LOG_NOTICE, "sum length too short in IPv4 AH input "
  194                     "(%lu, should be at least %lu): %s\n",
  195                     (u_long)siz1, (u_long)siz,
  196                     ipsec4_logpacketstr(ip, spi)));
  197                 ipsecstat.in_inval++;
  198                 goto fail;
  199         }
  200         if ((ah->ah_len << 2) - sizoff != siz1) {
  201                 ipseclog((LOG_NOTICE, "sum length mismatch in IPv4 AH input "
  202                     "(%d should be %lu): %s\n",
  203                     (ah->ah_len << 2) - sizoff, (u_long)siz1,
  204                     ipsec4_logpacketstr(ip, spi)));
  205                 ipsecstat.in_inval++;
  206                 goto fail;
  207         }
  208         if (siz1 > sizeof(cksum)) {
  209                 ipseclog((LOG_NOTICE, "sum length too large: %s\n",
  210                     ipsec4_logpacketstr(ip, spi)));
  211                 ipsecstat.in_inval++;
  212                 goto fail;
  213         }
  214 
  215         IP6_EXTHDR_GET(ah, struct ah *, m, off,
  216                 sizeof(struct ah) + sizoff + siz1);
  217         if (ah == NULL) {
  218                 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n"));
  219                 ipsecstat.in_inval++;
  220                 goto fail;
  221         }
  222     }
  223 
  224         /*
  225          * check for sequence number.
  226          */
  227         if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
  228                 if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
  229                         ; /* okey */
  230                 else {
  231                         ipsecstat.in_ahreplay++;
  232                         ipseclog((LOG_WARNING,
  233                             "replay packet in IPv4 AH input: %s %s\n",
  234                             ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
  235                         goto fail;
  236                 }
  237         }
  238 
  239         /*
  240          * alright, it seems sane.  now we are going to check the
  241          * cryptographic checksum.
  242          */
  243 
  244     {
  245 #if 0
  246         /*
  247          * some of IP header fields are flipped to the host endian.
  248          * convert them back to network endian.  VERY stupid.
  249          */
  250         ip->ip_len = htons(ip->ip_len);
  251         ip->ip_off = htons(ip->ip_off);
  252 #endif
  253         if (ah4_calccksum(m, cksum, siz1, algo, sav)) {
  254                 ipsecstat.in_inval++;
  255                 goto fail;
  256         }
  257         ipsecstat.in_ahhist[sav->alg_auth]++;
  258 #if 0
  259         /*
  260          * flip them back.
  261          */
  262         ip->ip_len = ntohs(ip->ip_len);
  263         ip->ip_off = ntohs(ip->ip_off);
  264 #endif
  265     }
  266 
  267     {
  268         caddr_t sumpos = NULL;
  269 
  270         if (sav->flags & SADB_X_EXT_OLD) {
  271                 /* RFC 1826 */
  272                 sumpos = (caddr_t)(ah + 1);
  273         } else {
  274                 /* RFC 2402 */
  275                 sumpos = (caddr_t)(((struct newah *)ah) + 1);
  276         }
  277 
  278         if (bcmp(sumpos, cksum, siz) != 0) {
  279                 ipseclog((LOG_WARNING,
  280                     "checksum mismatch in IPv4 AH input: %s %s\n",
  281                     ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
  282                 ipsecstat.in_ahauthfail++;
  283                 goto fail;
  284         }
  285     }
  286 
  287         m->m_flags |= M_AUTHIPHDR;
  288         m->m_flags |= M_AUTHIPDGM;
  289 
  290 #if 0
  291         /*
  292          * looks okey, but we need more sanity check.
  293          * XXX should elaborate.
  294          */
  295         if (ah->ah_nxt == IPPROTO_IPIP || ah->ah_nxt == IPPROTO_IP) {
  296                 struct ip *nip;
  297                 size_t sizoff;
  298 
  299                 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
  300 
  301                 if (m->m_len < off + sizeof(struct ah) + sizoff + siz1 + hlen) {
  302                         m = m_pullup(m, off + sizeof(struct ah)
  303                                         + sizoff + siz1 + hlen);
  304                         if (!m) {
  305                                 ipseclog((LOG_DEBUG,
  306                                     "IPv4 AH input: can't pullup\n"));
  307                                 ipsecstat.in_inval++;
  308                                 goto fail;
  309                         }
  310                 }
  311 
  312                 nip = (struct ip *)((u_char *)(ah + 1) + sizoff + siz1);
  313                 if (nip->ip_src.s_addr != ip->ip_src.s_addr
  314                  || nip->ip_dst.s_addr != ip->ip_dst.s_addr) {
  315                         m->m_flags &= ~M_AUTHIPHDR;
  316                         m->m_flags &= ~M_AUTHIPDGM;
  317                 }
  318         }
  319 #ifdef INET6
  320         else if (ah->ah_nxt == IPPROTO_IPV6) {
  321                 m->m_flags &= ~M_AUTHIPHDR;
  322                 m->m_flags &= ~M_AUTHIPDGM;
  323         }
  324 #endif /* INET6 */
  325 #endif /* 0 */
  326 
  327         if (m->m_flags & M_AUTHIPHDR && m->m_flags & M_AUTHIPDGM) {
  328 #if 0
  329                 ipseclog((LOG_DEBUG,
  330                     "IPv4 AH input: authentication succeess\n"));
  331 #endif
  332                 ipsecstat.in_ahauthsucc++;
  333         } else {
  334                 ipseclog((LOG_WARNING,
  335                     "authentication failed in IPv4 AH input: %s %s\n",
  336                     ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
  337                 ipsecstat.in_ahauthfail++;
  338                 goto fail;
  339         }
  340 
  341         /*
  342          * update sequence number.
  343          */
  344         if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
  345                 if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
  346                         ipsecstat.in_ahreplay++;
  347                         goto fail;
  348                 }
  349         }
  350 
  351         /* was it transmitted over the IPsec tunnel SA? */
  352         if (sav->flags & SADB_X_EXT_OLD) {
  353                 /* RFC 1826 */
  354                 stripsiz = sizeof(struct ah) + siz1;
  355         } else {
  356                 /* RFC 2402 */
  357                 stripsiz = sizeof(struct newah) + siz1;
  358         }
  359         if (ipsec4_tunnel_validate(ip, nxt, sav)) {
  360                 /*
  361                  * strip off all the headers that precedes AH.
  362                  *      IP xx AH IP' payload -> IP' payload
  363                  *
  364                  * XXX more sanity checks
  365                  * XXX relationship with gif?
  366                  */
  367                 u_int8_t tos;
  368 
  369                 tos = ip->ip_tos;
  370                 m_adj(m, off + stripsiz);
  371                 if (m->m_len < sizeof(*ip)) {
  372                         m = m_pullup(m, sizeof(*ip));
  373                         if (!m) {
  374                                 ipsecstat.in_inval++;
  375                                 goto fail;
  376                         }
  377                 }
  378                 ip = mtod(m, struct ip *);
  379                 /* ECN consideration. */
  380                 ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos);
  381                 if (!key_checktunnelsanity(sav, AF_INET,
  382                             (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) {
  383                         ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
  384                             "in IPv4 AH input: %s %s\n",
  385                             ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
  386                         ipsecstat.in_inval++;
  387                         goto fail;
  388                 }
  389 
  390                 key_sa_recordxfer(sav, m);
  391                 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 ||
  392                     ipsec_addhist(m, IPPROTO_IPV4, 0) != 0) {
  393                         ipsecstat.in_nomem++;
  394                         goto fail;
  395                 }
  396 
  397                 s = splnet();
  398                 if (IF_QFULL(&ipintrq)) {
  399                         ipsecstat.in_inval++;
  400                         splx(s);
  401                         goto fail;
  402                 }
  403                 IF_ENQUEUE(&ipintrq, m);
  404                 m = NULL;
  405                 schednetisr(NETISR_IP); /* can be skipped but to make sure */
  406                 splx(s);
  407                 nxt = IPPROTO_DONE;
  408         } else {
  409                 /*
  410                  * strip off AH.
  411                  */
  412 
  413                 ip = mtod(m, struct ip *);
  414                 /*
  415                  * even in m_pulldown case, we need to strip off AH so that
  416                  * we can compute checksum for multiple AH correctly.
  417                  */
  418                 if (m->m_len >= stripsiz + off) {
  419                         ovbcopy((caddr_t)ip, ((caddr_t)ip) + stripsiz, off);
  420                         m->m_data += stripsiz;
  421                         m->m_len -= stripsiz;
  422                         m->m_pkthdr.len -= stripsiz;
  423                 } else {
  424                         /*
  425                          * this comes with no copy if the boundary is on
  426                          * cluster
  427                          */
  428                         struct mbuf *n;
  429 
  430                         n = m_split(m, off, M_DONTWAIT);
  431                         if (n == NULL) {
  432                                 /* m is retained by m_split */
  433                                 goto fail;
  434                         }
  435                         m_adj(n, stripsiz);
  436                         /* m_cat does not update m_pkthdr.len */
  437                         m->m_pkthdr.len += n->m_pkthdr.len;
  438                         m_cat(m, n);
  439                 }
  440 
  441                 if (m->m_len < sizeof(*ip)) {
  442                         m = m_pullup(m, sizeof(*ip));
  443                         if (m == NULL) {
  444                                 ipsecstat.in_inval++;
  445                                 goto fail;
  446                         }
  447                 }
  448                 ip = mtod(m, struct ip *);
  449 #ifdef IPLEN_FLIPPED
  450                 ip->ip_len = ip->ip_len - stripsiz;
  451 #else
  452                 ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz);
  453 #endif
  454                 ip->ip_p = nxt;
  455                 /* forget about IP hdr checksum, the check has already been passed */
  456 
  457                 key_sa_recordxfer(sav, m);
  458                 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
  459                         ipsecstat.in_nomem++;
  460                         goto fail;
  461                 }
  462 
  463                 if (nxt != IPPROTO_DONE) {
  464                         if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 &&
  465                             ipsec4_in_reject(m, NULL)) {
  466                                 ipsecstat.in_polvio++;
  467                                 goto fail;
  468                         }
  469                         (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt);
  470                 } else
  471                         m_freem(m);
  472                 m = NULL;
  473         }
  474 
  475         if (sav) {
  476                 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
  477                         printf("DP ah4_input call free SA:%p\n", sav));
  478                 key_freesav(sav);
  479         }
  480         ipsecstat.in_success++;
  481         return;
  482 
  483 fail:
  484         if (sav) {
  485                 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
  486                         printf("DP ah4_input call free SA:%p\n", sav));
  487                 key_freesav(sav);
  488         }
  489         if (m)
  490                 m_freem(m);
  491         return;
  492 }
  493 
  494 /* assumes that ip header and ah header are contiguous on mbuf */
  495 void *
  496 ah4_ctlinput(cmd, sa, v)
  497         int cmd;
  498         struct sockaddr *sa;
  499         void *v;
  500 {
  501         struct ip *ip = v;
  502         struct ah *ah;
  503         struct icmp *icp;
  504         struct secasvar *sav;
  505 
  506         if (sa->sa_family != AF_INET ||
  507             sa->sa_len != sizeof(struct sockaddr_in))
  508                 return NULL;
  509         if ((unsigned)cmd >= PRC_NCMDS)
  510                 return NULL;
  511         if (cmd == PRC_MSGSIZE && ip_mtudisc && ip && ip->ip_v == 4) {
  512                 /*
  513                  * Check to see if we have a valid SA corresponding to
  514                  * the address in the ICMP message payload.
  515                  */
  516                 ah = (struct ah *)((caddr_t)ip + (ip->ip_hl << 2));
  517                 if ((sav = key_allocsa(AF_INET,
  518                                        (caddr_t) &ip->ip_src,
  519                                        (caddr_t) &ip->ip_dst,
  520                                        IPPROTO_AH, ah->ah_spi)) == NULL)
  521                         return NULL;
  522                 if (sav->state != SADB_SASTATE_MATURE &&
  523                     sav->state != SADB_SASTATE_DYING) {
  524                         key_freesav(sav);
  525                         return NULL;
  526                 }
  527 
  528                 /* XXX Further validation? */
  529 
  530                 key_freesav(sav);
  531 
  532                 /*
  533                  * Now that we've validated that we are actually communicating
  534                  * with the host indicated in the ICMP message, locate the
  535                  * ICMP header, recalculate the new MTU, and create the
  536                  * corresponding routing entry.
  537                  */
  538                 icp = (struct icmp *)((caddr_t)ip -
  539                     offsetof(struct icmp, icmp_ip));
  540                 icmp_mtudisc(icp, ip->ip_dst);
  541 
  542                 return NULL;
  543         }
  544 
  545         return NULL;
  546 }
  547 #endif /* INET */
  548 
  549 #ifdef INET6
  550 int
  551 ah6_input(mp, offp, proto)
  552         struct mbuf **mp;
  553         int *offp, proto;
  554 {
  555         struct mbuf *m = *mp;
  556         int off = *offp;
  557         struct ip6_hdr *ip6;
  558         struct ah *ah;
  559         u_int32_t spi;
  560         const struct ah_algorithm *algo;
  561         size_t siz;
  562         size_t siz1;
  563         u_int8_t cksum[AH_MAXSUMSIZE];
  564         struct secasvar *sav = NULL;
  565         u_int16_t nxt;
  566         int s;
  567         size_t stripsiz = 0;
  568 
  569         IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
  570         if (ah == NULL) {
  571                 ipseclog((LOG_DEBUG, "IPv6 AH input: can't pullup\n"));
  572                 ipsec6stat.in_inval++;
  573                 return IPPROTO_DONE;
  574         }
  575         ip6 = mtod(m, struct ip6_hdr *);
  576         nxt = ah->ah_nxt;
  577 
  578         /* find the sassoc. */
  579         spi = ah->ah_spi;
  580 
  581         if (ntohs(ip6->ip6_plen) == 0) {
  582                 ipseclog((LOG_ERR, "IPv6 AH input: "
  583                     "AH with IPv6 jumbogram is not supported.\n"));
  584                 ipsec6stat.in_inval++;
  585                 goto fail;
  586         }
  587 
  588         if ((sav = key_allocsa(AF_INET6,
  589                               (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst,
  590                               IPPROTO_AH, spi)) == 0) {
  591                 ipseclog((LOG_WARNING,
  592                     "IPv6 AH input: no key association found for spi %u\n",
  593                     (u_int32_t)ntohl(spi)));
  594                 ipsec6stat.in_nosa++;
  595                 goto fail;
  596         }
  597         KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
  598                 printf("DP ah6_input called to allocate SA:%p\n", sav));
  599         if (sav->state != SADB_SASTATE_MATURE &&
  600             sav->state != SADB_SASTATE_DYING) {
  601                 ipseclog((LOG_DEBUG,
  602                     "IPv6 AH input: non-mature/dying SA found for spi %u; ",
  603                     (u_int32_t)ntohl(spi)));
  604                 ipsec6stat.in_badspi++;
  605                 goto fail;
  606         }
  607 
  608         algo = ah_algorithm_lookup(sav->alg_auth);
  609         if (!algo) {
  610                 ipseclog((LOG_DEBUG, "IPv6 AH input: "
  611                     "unsupported authentication algorithm for spi %u\n",
  612                     (u_int32_t)ntohl(spi)));
  613                 ipsec6stat.in_badspi++;
  614                 goto fail;
  615         }
  616 
  617         siz = (*algo->sumsiz)(sav);
  618         siz1 = ((siz + 3) & ~(4 - 1));
  619 
  620         /*
  621          * sanity checks for header, 1.
  622          */
  623     {
  624         int sizoff;
  625 
  626         sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
  627 
  628         /*
  629          * Here, we do not do "siz1 == siz".  See ah4_input() for complete
  630          * description.
  631          */
  632         if (siz1 < siz) {
  633                 ipseclog((LOG_NOTICE, "sum length too short in IPv6 AH input "
  634                     "(%lu, should be at least %lu): %s\n",
  635                     (u_long)siz1, (u_long)siz,
  636                     ipsec6_logpacketstr(ip6, spi)));
  637                 ipsec6stat.in_inval++;
  638                 goto fail;
  639         }
  640         if ((ah->ah_len << 2) - sizoff != siz1) {
  641                 ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input "
  642                     "(%d should be %lu): %s\n",
  643                     (ah->ah_len << 2) - sizoff, (u_long)siz1,
  644                     ipsec6_logpacketstr(ip6, spi)));
  645                 ipsec6stat.in_inval++;
  646                 goto fail;
  647         }
  648         if (siz1 > sizeof(cksum)) {
  649                 ipseclog((LOG_NOTICE, "sum length too large: %s\n",
  650                     ipsec6_logpacketstr(ip6, spi)));
  651                 ipsec6stat.in_inval++;
  652                 goto fail;
  653         }
  654 
  655         IP6_EXTHDR_GET(ah, struct ah *, m, off,
  656                 sizeof(struct ah) + sizoff + siz1);
  657         if (ah == NULL) {
  658                 ipseclog((LOG_NOTICE, "couldn't pullup gather IPv6 AH checksum part"));
  659                 ipsec6stat.in_inval++;
  660                 m = NULL;
  661                 goto fail;
  662         }
  663     }
  664 
  665         /*
  666          * check for sequence number.
  667          */
  668         if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
  669                 if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
  670                         ; /* okey */
  671                 else {
  672                         ipsec6stat.in_ahreplay++;
  673                         ipseclog((LOG_WARNING,
  674                             "replay packet in IPv6 AH input: %s %s\n",
  675                             ipsec6_logpacketstr(ip6, spi),
  676                             ipsec_logsastr(sav)));
  677                         goto fail;
  678                 }
  679         }
  680 
  681         /*
  682          * alright, it seems sane.  now we are going to check the
  683          * cryptographic checksum.
  684          */
  685 
  686         if (ah6_calccksum(m, cksum, siz1, algo, sav)) {
  687                 ipsec6stat.in_inval++;
  688                 goto fail;
  689         }
  690         ipsec6stat.in_ahhist[sav->alg_auth]++;
  691 
  692     {
  693         caddr_t sumpos = NULL;
  694 
  695         if (sav->flags & SADB_X_EXT_OLD) {
  696                 /* RFC 1826 */
  697                 sumpos = (caddr_t)(ah + 1);
  698         } else {
  699                 /* RFC 2402 */
  700                 sumpos = (caddr_t)(((struct newah *)ah) + 1);
  701         }
  702 
  703         if (bcmp(sumpos, cksum, siz) != 0) {
  704                 ipseclog((LOG_WARNING,
  705                     "checksum mismatch in IPv6 AH input: %s %s\n",
  706                     ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
  707                 ipsec6stat.in_ahauthfail++;
  708                 goto fail;
  709         }
  710     }
  711 
  712         m->m_flags |= M_AUTHIPHDR;
  713         m->m_flags |= M_AUTHIPDGM;
  714 
  715 #if 0
  716         /*
  717          * looks okey, but we need more sanity check.
  718          * XXX should elaborate.
  719          */
  720         if (ah->ah_nxt == IPPROTO_IPV6) {
  721                 struct ip6_hdr *nip6;
  722                 size_t sizoff;
  723 
  724                 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
  725 
  726                 IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1
  727                                 + sizeof(struct ip6_hdr), IPPROTO_DONE);
  728 
  729                 nip6 = (struct ip6_hdr *)((u_char *)(ah + 1) + sizoff + siz1);
  730                 if (!IN6_ARE_ADDR_EQUAL(&nip6->ip6_src, &ip6->ip6_src)
  731                  || !IN6_ARE_ADDR_EQUAL(&nip6->ip6_dst, &ip6->ip6_dst)) {
  732                         m->m_flags &= ~M_AUTHIPHDR;
  733                         m->m_flags &= ~M_AUTHIPDGM;
  734                 }
  735         } else if (ah->ah_nxt == IPPROTO_IPIP) {
  736                 m->m_flags &= ~M_AUTHIPHDR;
  737                 m->m_flags &= ~M_AUTHIPDGM;
  738         } else if (ah->ah_nxt == IPPROTO_IP) {
  739                 m->m_flags &= ~M_AUTHIPHDR;
  740                 m->m_flags &= ~M_AUTHIPDGM;
  741         }
  742 #endif
  743 
  744         if (m->m_flags & M_AUTHIPHDR && m->m_flags & M_AUTHIPDGM) {
  745 #if 0
  746                 ipseclog((LOG_DEBUG,
  747                     "IPv6 AH input: authentication succeess\n"));
  748 #endif
  749                 ipsec6stat.in_ahauthsucc++;
  750         } else {
  751                 ipseclog((LOG_WARNING,
  752                     "authentication failed in IPv6 AH input: %s %s\n",
  753                     ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
  754                 ipsec6stat.in_ahauthfail++;
  755                 goto fail;
  756         }
  757 
  758         /*
  759          * update sequence number.
  760          */
  761         if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
  762                 if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
  763                         ipsec6stat.in_ahreplay++;
  764                         goto fail;
  765                 }
  766         }
  767 
  768         /* was it transmitted over the IPsec tunnel SA? */
  769         if (sav->flags & SADB_X_EXT_OLD) {
  770                 /* RFC 1826 */
  771                 stripsiz = sizeof(struct ah) + siz1;
  772         } else {
  773                 /* RFC 2402 */
  774                 stripsiz = sizeof(struct newah) + siz1;
  775         }
  776         if (ipsec6_tunnel_validate(ip6, nxt, sav)) {
  777                 /*
  778                  * strip off all the headers that precedes AH.
  779                  *      IP6 xx AH IP6' payload -> IP6' payload
  780                  *
  781                  * XXX more sanity checks
  782                  * XXX relationship with gif?
  783                  */
  784                 u_int32_t flowinfo;     /* net endian */
  785 
  786                 flowinfo = ip6->ip6_flow;
  787                 m_adj(m, off + stripsiz);
  788                 if (m->m_len < sizeof(*ip6)) {
  789                         m = m_pullup(m, sizeof(*ip6));
  790                         if (!m) {
  791                                 ipsec6stat.in_inval++;
  792                                 goto fail;
  793                         }
  794                 }
  795                 ip6 = mtod(m, struct ip6_hdr *);
  796                 /* ECN consideration. */
  797                 ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow);
  798                 if (!key_checktunnelsanity(sav, AF_INET6,
  799                             (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) {
  800                         ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
  801                             "in IPv6 AH input: %s %s\n",
  802                             ipsec6_logpacketstr(ip6, spi),
  803                             ipsec_logsastr(sav)));
  804                         ipsec6stat.in_inval++;
  805                         goto fail;
  806                 }
  807 
  808                 key_sa_recordxfer(sav, m);
  809                 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 ||
  810                     ipsec_addhist(m, IPPROTO_IPV6, 0) != 0) {
  811                         ipsec6stat.in_nomem++;
  812                         goto fail;
  813                 }
  814 
  815                 s = splnet();
  816                 if (IF_QFULL(&ip6intrq)) {
  817                         ipsec6stat.in_inval++;
  818                         splx(s);
  819                         goto fail;
  820                 }
  821                 IF_ENQUEUE(&ip6intrq, m);
  822                 m = NULL;
  823                 schednetisr(NETISR_IPV6); /* can be skipped but to make sure */
  824                 splx(s);
  825                 nxt = IPPROTO_DONE;
  826         } else {
  827                 /*
  828                  * strip off AH.
  829                  */
  830                 u_int8_t *prvnxtp;
  831 
  832                 /*
  833                  * Copy the value of the next header field of AH to the
  834                  * next header field of the previous header.
  835                  * This is necessary because AH will be stripped off below.
  836                  */
  837                 prvnxtp = ip6_get_prevhdr(m, off); /* XXX */
  838                 *prvnxtp = nxt;
  839 
  840                 ip6 = mtod(m, struct ip6_hdr *);
  841                 /*
  842                  * even in m_pulldown case, we need to strip off AH so that
  843                  * we can compute checksum for multiple AH correctly.
  844                  */
  845                 if (m->m_len >= stripsiz + off) {
  846                         ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
  847                         m->m_data += stripsiz;
  848                         m->m_len -= stripsiz;
  849                         m->m_pkthdr.len -= stripsiz;
  850                 } else {
  851                         /*
  852                          * this comes with no copy if the boundary is on
  853                          * cluster
  854                          */
  855                         struct mbuf *n;
  856 
  857                         n = m_split(m, off, M_DONTWAIT);
  858                         if (n == NULL) {
  859                                 /* m is retained by m_split */
  860                                 goto fail;
  861                         }
  862                         m_adj(n, stripsiz);
  863                         /* m_cat does not update m_pkthdr.len */
  864                         m->m_pkthdr.len += n->m_pkthdr.len;
  865                         m_cat(m, n);
  866                 }
  867                 ip6 = mtod(m, struct ip6_hdr *);
  868                 /* XXX jumbogram */
  869                 ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz);
  870 
  871                 key_sa_recordxfer(sav, m);
  872                 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
  873                         ipsec6stat.in_nomem++;
  874                         goto fail;
  875                 }
  876         }
  877 
  878         *offp = off;
  879         *mp = m;
  880 
  881         if (sav) {
  882                 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
  883                         printf("DP ah6_input call free SA:%p\n", sav));
  884                 key_freesav(sav);
  885         }
  886         ipsec6stat.in_success++;
  887         return nxt;
  888 
  889 fail:
  890         if (sav) {
  891                 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
  892                         printf("DP ah6_input call free SA:%p\n", sav));
  893                 key_freesav(sav);
  894         }
  895         if (m)
  896                 m_freem(m);
  897         return IPPROTO_DONE;
  898 }
  899 
  900 void
  901 ah6_ctlinput(cmd, sa, d)
  902         int cmd;
  903         struct sockaddr *sa;
  904         void *d;
  905 {
  906         const struct newah *ahp;
  907         struct newah ah;
  908         struct secasvar *sav;
  909         struct ip6_hdr *ip6;
  910         struct mbuf *m;
  911         struct ip6ctlparam *ip6cp = NULL;
  912         int off;
  913         struct sockaddr_in6 *sa6_src, *sa6_dst;
  914 
  915         if (sa->sa_family != AF_INET6 ||
  916             sa->sa_len != sizeof(struct sockaddr_in6))
  917                 return;
  918         if ((unsigned)cmd >= PRC_NCMDS)
  919                 return;
  920 
  921         /* if the parameter is from icmp6, decode it. */
  922         if (d != NULL) {
  923                 ip6cp = (struct ip6ctlparam *)d;
  924                 m = ip6cp->ip6c_m;
  925                 ip6 = ip6cp->ip6c_ip6;
  926                 off = ip6cp->ip6c_off;
  927         } else {
  928                 m = NULL;
  929                 ip6 = NULL;
  930                 off = 0;
  931         }
  932 
  933         if (ip6) {
  934                 /*
  935                  * XXX: We assume that when ip6 is non NULL,
  936                  * M and OFF are valid.
  937                  */
  938 
  939                 /* check if we can safely examine src and dst ports */
  940                 if (m->m_pkthdr.len < off + sizeof(ah))
  941                         return;
  942 
  943                 if (m->m_len < off + sizeof(ah)) {
  944                         /*
  945                          * this should be rare case,
  946                          * so we compromise on this copy...
  947                          */
  948                         m_copydata(m, off, sizeof(ah), (caddr_t)&ah);
  949                         ahp = &ah;
  950                 } else
  951                         ahp = (struct newah *)(mtod(m, caddr_t) + off);
  952 
  953                 if (cmd == PRC_MSGSIZE) {
  954                         int valid = 0;
  955 
  956                         /*
  957                          * Check to see if we have a valid SA corresponding to
  958                          * the address in the ICMP message payload.
  959                          */
  960                         sa6_src = ip6cp->ip6c_src;
  961                         sa6_dst = (struct sockaddr_in6 *)sa;
  962                         sav = key_allocsa(AF_INET6,
  963                                           (caddr_t)&sa6_src->sin6_addr,
  964                                           (caddr_t)&sa6_dst->sin6_addr,
  965                                           IPPROTO_AH, ahp->ah_spi);
  966                         if (sav) {
  967                                 if (sav->state == SADB_SASTATE_MATURE ||
  968                                     sav->state == SADB_SASTATE_DYING)
  969                                         valid++;
  970                                 key_freesav(sav);
  971                         }
  972 
  973                         /* XXX Further validation? */
  974 
  975                         /*
  976                          * Depending on the value of "valid" and routing table
  977                          * size (mtudisc_{hi,lo}wat), we will:
  978                          * - recalcurate the new MTU and create the
  979                          *   corresponding routing entry, or
  980                          * - ignore the MTU change notification.
  981                          */
  982                         icmp6_mtudisc_update((struct ip6ctlparam *)d, valid);
  983                 }
  984 
  985                 /* we normally notify single pcb here */
  986         } else {
  987                 /* we normally notify any pcb here */
  988         }
  989 }
  990 #endif /* INET6 */

Cache object: d35e164f045b6bee3266d67a32134d33


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.