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

Cache object: 2f0b5a911425b48668907ff92506f7ca


[ 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.