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/esp_output.c

Version: -  FREEBSD  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: esp_output.c,v 1.32 2008/04/23 06:09:05 thorpej Exp $  */
    2 /*      $KAME: esp_output.c,v 1.44 2001/07/26 06:53:15 jinmei 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  * RFC1827/2406 Encapsulated Security Payload.
   35  */
   36 
   37 #include <sys/cdefs.h>
   38 __KERNEL_RCSID(0, "$NetBSD: esp_output.c,v 1.32 2008/04/23 06:09:05 thorpej 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/socketvar.h>
   51 #include <sys/errno.h>
   52 #include <sys/time.h>
   53 #include <sys/kernel.h>
   54 #include <sys/syslog.h>
   55 
   56 #include <net/if.h>
   57 #include <net/route.h>
   58 
   59 #include <netinet/in.h>
   60 #include <netinet/in_systm.h>
   61 #include <netinet/ip.h>
   62 #include <netinet/in_var.h>
   63 #ifdef IPSEC_NAT_T
   64 #include <netinet/udp.h>
   65 #endif
   66 
   67 #ifdef INET6
   68 #include <netinet/ip6.h>
   69 #include <netinet6/ip6_var.h>
   70 #include <netinet/icmp6.h>
   71 #endif
   72 
   73 #include <netinet6/ipsec.h>
   74 #include <netinet6/ipsec_private.h>
   75 #include <netinet6/ah.h>
   76 #include <netinet6/esp.h>
   77 #include <netkey/key.h>
   78 #include <netkey/keydb.h>
   79 
   80 #include <net/net_osdep.h>
   81 
   82 static int esp_output __P((struct mbuf *, u_char *, struct mbuf *,
   83         struct ipsecrequest *, int));
   84 
   85 /*
   86  * compute ESP header size.
   87  */
   88 size_t
   89 esp_hdrsiz(struct ipsecrequest *isr)
   90 {
   91         struct secasvar *sav;
   92         const struct esp_algorithm *algo;
   93         const struct ah_algorithm *aalgo;
   94         size_t ivlen;
   95         size_t authlen;
   96         size_t hdrsiz;
   97 
   98         /* sanity check */
   99         if (isr == NULL)
  100                 panic("esp_hdrsiz: NULL was passed.");
  101 
  102         sav = isr->sav;
  103 
  104         if (isr->saidx.proto != IPPROTO_ESP)
  105                 panic("unsupported mode passed to esp_hdrsiz");
  106 
  107         if (sav == NULL)
  108                 goto estimate;
  109         if (sav->state != SADB_SASTATE_MATURE
  110          && sav->state != SADB_SASTATE_DYING)
  111                 goto estimate;
  112 
  113         /* we need transport mode ESP. */
  114         algo = esp_algorithm_lookup(sav->alg_enc);
  115         if (!algo)
  116                 goto estimate;
  117         ivlen = sav->ivlen;
  118 #if 0
  119         if (ivlen < 0)
  120                 goto estimate;
  121 #endif
  122 
  123         /*
  124          * XXX
  125          * right now we don't calcurate the padding size.  simply
  126          * treat the padding size as constant, for simplicity.
  127          *
  128          * XXX variable size padding support
  129          */
  130         if (sav->flags & SADB_X_EXT_OLD) {
  131                 /* RFC 1827 */
  132                 hdrsiz = sizeof(struct esp) + ivlen +
  133                     esp_max_padbound() - 1 + 2;
  134         } else {
  135                 /* RFC 2406 */
  136                 aalgo = ah_algorithm_lookup(sav->alg_auth);
  137                 if (aalgo && sav->replay && sav->key_auth)
  138                         authlen = (aalgo->sumsiz)(sav);
  139                 else
  140                         authlen = 0;
  141                 hdrsiz = sizeof(struct newesp) + ivlen +
  142                     esp_max_padbound() - 1 + 2 + authlen;
  143         }
  144 
  145 #ifdef IPSEC_NAT_T
  146         /*
  147          * If NAT-T is enabled, add the space for UDP encapsulation
  148          */
  149         if (sav->natt_type != 0) {
  150                 hdrsiz += sizeof(struct udphdr);
  151                 if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE)
  152                         hdrsiz += sizeof(u_int64_t);
  153         }
  154 #endif
  155 
  156         return hdrsiz;
  157 
  158    estimate:
  159         /*
  160          * ASSUMING:
  161          *      sizeof(struct newesp) > sizeof(struct esp).
  162          *      esp_max_ivlen() = max ivlen for CBC mode
  163          *      esp_max_padbound - 1 =
  164          *         maximum padding length without random padding length
  165          *      2 = (Pad Length field) + (Next Header field).
  166          *      AH_MAXSUMSIZE = maximum ICV we support.
  167          *      sizeof(u_int64_t) = non IKE marker (NAT-T)
  168          *      sizeof(struct udphdr) = UDP encapsulation (NAT-T)
  169          */
  170         return sizeof(struct newesp) + esp_max_ivlen() +
  171             esp_max_padbound() - 1 + 2 + AH_MAXSUMSIZE +
  172 #ifdef IPSEC_NAT_T
  173             sizeof(u_int64_t) + sizeof(struct udphdr) +
  174 #endif
  175             0;
  176 }
  177 
  178 /*
  179  * Modify the packet so that the payload is encrypted.
  180  * The mbuf (m) must start with IPv4 or IPv6 header.
  181  * On failure, free the given mbuf and return NULL.
  182  *
  183  * on invocation:
  184  *      m   nexthdrp md
  185  *      v   v        v
  186  *      IP ......... payload
  187  * during the encryption:
  188  *      m   nexthdrp mprev md
  189  *      v   v        v     v
  190  *      IP ............... esp iv payload pad padlen nxthdr
  191  *                         <--><-><------><--------------->
  192  *                         esplen plen    extendsiz
  193  *                             ivlen
  194  *                         <-----> esphlen
  195  *      <-> hlen
  196  *      <-----------------> espoff
  197  */
  198 static int
  199 esp_output(struct mbuf *m, u_char *nexthdrp, struct mbuf *md, 
  200         struct ipsecrequest *isr, int af)
  201 {
  202         struct mbuf *n;
  203         struct mbuf *mprev;
  204         struct esp *esp;
  205         struct esptail *esptail;
  206         struct secasvar *sav = isr->sav;
  207         const struct esp_algorithm *algo;
  208         u_int32_t spi;
  209         u_int8_t nxt = 0;
  210         size_t plen;    /* payload length to be encrypted */
  211         size_t espoff;
  212         int ivlen;
  213         int afnumber;
  214         size_t extendsiz;
  215         int error = 0;
  216         percpu_t *stat;
  217 #ifdef IPSEC_NAT_T
  218         struct udphdr *udp = NULL;
  219 #endif
  220 
  221         switch (af) {
  222 #ifdef INET
  223         case AF_INET:
  224                 afnumber = 4;
  225                 stat = ipsecstat_percpu;
  226                 break;
  227 #endif
  228 #ifdef INET6
  229         case AF_INET6:
  230                 afnumber = 6;
  231                 stat = ipsec6stat_percpu;
  232                 break;
  233 #endif
  234         default:
  235                 ipseclog((LOG_ERR, "esp_output: unsupported af %d\n", af));
  236                 return 0;       /* no change at all */
  237         }
  238 
  239         /* some sanity check */
  240         if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
  241                 switch (af) {
  242 #ifdef INET
  243                 case AF_INET:
  244                     {
  245                         struct ip *ip;
  246 
  247                         ip = mtod(m, struct ip *);
  248                         ipseclog((LOG_DEBUG, "esp4_output: internal error: "
  249                                 "sav->replay is null: %x->%x, SPI=%u\n",
  250                                 (u_int32_t)ntohl(ip->ip_src.s_addr),
  251                                 (u_int32_t)ntohl(ip->ip_dst.s_addr),
  252                                 (u_int32_t)ntohl(sav->spi)));
  253                         IPSEC_STATINC(IPSEC_STAT_OUT_INVAL);
  254                         break;
  255                     }
  256 #endif /* INET */
  257 #ifdef INET6
  258                 case AF_INET6:
  259                         ipseclog((LOG_DEBUG, "esp6_output: internal error: "
  260                                 "sav->replay is null: SPI=%u\n",
  261                                 (u_int32_t)ntohl(sav->spi)));
  262                         IPSEC6_STATINC(IPSEC_STAT_OUT_INVAL);
  263                         break;
  264 #endif /* INET6 */
  265                 default:
  266                         panic("esp_output: should not reach here");
  267                 }
  268                 error = EINVAL;
  269                 goto fail;
  270         }
  271 
  272         algo = esp_algorithm_lookup(sav->alg_enc);
  273         if (!algo) {
  274                 ipseclog((LOG_ERR, "esp_output: unsupported algorithm: "
  275                     "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
  276                 error = EINVAL;
  277                 goto fail;
  278         }
  279         spi = sav->spi;
  280         ivlen = sav->ivlen;
  281         /* should be okey */
  282         if (ivlen < 0) {
  283                 panic("invalid ivlen");
  284         }
  285 
  286     {
  287         /*
  288          * insert ESP header.
  289          * XXX inserts ESP header right after IPv4 header.  should
  290          * chase the header chain.
  291          * XXX sequential number
  292          */
  293 #ifdef INET
  294         struct ip *ip = NULL;
  295 #endif
  296 #ifdef INET6
  297         struct ip6_hdr *ip6 = NULL;
  298 #endif
  299         size_t esplen;  /* sizeof(struct esp/newesp) */
  300         size_t esphlen; /* sizeof(struct esp/newesp) + ivlen */
  301         size_t hlen = 0;        /* ip header len */
  302 
  303         if (sav->flags & SADB_X_EXT_OLD) {
  304                 /* RFC 1827 */
  305                 esplen = sizeof(struct esp);
  306         } else {
  307                 /* RFC 2406 */
  308                 if (sav->flags & SADB_X_EXT_DERIV)
  309                         esplen = sizeof(struct esp);
  310                 else
  311                         esplen = sizeof(struct newesp);
  312         }
  313         esphlen = esplen + ivlen;
  314 
  315         for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
  316                 ;
  317         if (mprev == NULL || mprev->m_next != md) {
  318                 ipseclog((LOG_DEBUG, "esp%d_output: md is not in chain\n",
  319                     afnumber));
  320                 error = EINVAL;
  321                 goto fail;
  322         }
  323 
  324         plen = 0;
  325         for (n = md; n; n = n->m_next)
  326                 plen += n->m_len;
  327 
  328         switch (af) {
  329 #ifdef INET
  330         case AF_INET:
  331                 ip = mtod(m, struct ip *);
  332                 hlen = ip->ip_hl << 2;
  333                 break;
  334 #endif
  335 #ifdef INET6
  336         case AF_INET6:
  337                 ip6 = mtod(m, struct ip6_hdr *);
  338                 hlen = sizeof(*ip6);
  339                 break;
  340 #endif
  341         }
  342 
  343         /* make the packet over-writable */
  344         mprev->m_next = NULL;
  345         if ((md = ipsec_copypkt(md)) == NULL) {
  346                 error = ENOBUFS;
  347                 goto fail;
  348         }
  349         mprev->m_next = md;
  350 
  351         espoff = m->m_pkthdr.len - plen;
  352 
  353 #ifdef IPSEC_NAT_T
  354         if (sav->natt_type != 0) {
  355                 esphlen += sizeof(struct udphdr);
  356                 espoff += sizeof(struct udphdr);
  357 
  358                 if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) {
  359                         /* NON-IKE marker */
  360                         esphlen += sizeof(u_int64_t);
  361                         espoff += sizeof(u_int64_t);
  362                 }
  363         }
  364 #endif
  365 
  366         /*
  367          * grow the mbuf to accommodate ESP header.
  368          * before: IP ... payload
  369          * after (without NAT-T):  IP ... ESP IV payload
  370          * after (with older NAT-T):  IP ... UDP non-IKE-marker ESP IV payload
  371          * after (with newer NAT-T):  IP ... UDP ESP IV payload
  372          */
  373         if (M_LEADINGSPACE(md) < esphlen || (md->m_flags & M_EXT) != 0) {
  374                 MGET(n, M_DONTWAIT, MT_DATA);
  375                 if (!n) {
  376                         error = ENOBUFS;
  377                         goto fail;
  378                 }
  379                 n->m_len = esphlen;
  380                 mprev->m_next = n;
  381                 n->m_next = md;
  382                 m->m_pkthdr.len += esphlen;
  383 
  384                 esp = mtod(n, struct esp *);
  385         } else {
  386                 md->m_len += esphlen;
  387                 md->m_data -= esphlen;
  388                 m->m_pkthdr.len += esphlen;
  389                 esp = mtod(md, struct esp *);
  390         }
  391 
  392 #ifdef IPSEC_NAT_T
  393         if (sav->natt_type != 0) {
  394                 udp = (struct udphdr *)esp;
  395                 esp = (struct esp *)(udp + 1);
  396 
  397                 if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) {
  398                         u_int64_t *data = (u_int64_t *)esp;
  399 
  400                         *data = 0; /* NON-IKE marker */
  401                         esp = (struct esp *)(data + 1);
  402                 }
  403         }
  404 #endif
  405 
  406         nxt = *nexthdrp;
  407         *nexthdrp = IPPROTO_ESP;
  408         switch (af) {
  409 #ifdef INET
  410         case AF_INET:
  411                 if (esphlen < (IP_MAXPACKET - ntohs(ip->ip_len)))
  412                         ip->ip_len = htons(ntohs(ip->ip_len) + esphlen);
  413                 else {
  414                         ipseclog((LOG_ERR,
  415                             "IPv4 ESP output: size exceeds limit\n"));
  416                         IPSEC_STATINC(IPSEC_STAT_OUT_INVAL);
  417                         error = EMSGSIZE;
  418                         goto fail;
  419                 }
  420                 break;
  421 #endif
  422 #ifdef INET6
  423         case AF_INET6:
  424                 /* total packet length will be computed in ip6_output() */
  425                 break;
  426 #endif
  427         }
  428     }
  429 
  430         /* initialize esp header. */
  431         esp->esp_spi = spi;
  432         if ((sav->flags & SADB_X_EXT_OLD) == 0) {
  433                 struct newesp *nesp;
  434                 nesp = (struct newesp *)esp;
  435                 if (sav->replay->count == ~0) {
  436                         if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
  437                                 /* XXX Is it noisy ? */
  438                                 ipseclog((LOG_WARNING,
  439                                     "replay counter overflowed. %s\n",
  440                                     ipsec_logsastr(sav)));
  441                                 _NET_STATINC(stat, IPSEC_STAT_OUT_INVAL);
  442                                 error = EINVAL;
  443                                 goto fail;
  444                         }
  445                 }
  446                 sav->replay->count++;
  447                 /*
  448                  * XXX sequence number must not be cycled, if the SA is
  449                  * installed by IKE daemon.
  450                  */
  451                 nesp->esp_seq = htonl(sav->replay->count & 0xffffffff);
  452         }
  453 
  454     {
  455         /*
  456          * find the last mbuf. make some room for ESP trailer.
  457          */
  458 #ifdef INET
  459         struct ip *ip = NULL;
  460 #endif
  461         size_t padbound;
  462         u_char *extend;
  463         int i;
  464 
  465         if (algo->padbound)
  466                 padbound = algo->padbound;
  467         else
  468                 padbound = 4;
  469         /* ESP packet, including nxthdr field, must be length of 4n */
  470         if (padbound < 4)
  471                 padbound = 4;
  472 
  473         extendsiz = padbound - (plen % padbound);
  474         if (extendsiz == 1)
  475                 extendsiz = padbound + 1;
  476 
  477         n = m;
  478         while (n->m_next)
  479                 n = n->m_next;
  480 
  481         /*
  482          * if M_EXT, the external mbuf data may be shared among
  483          * two consequtive TCP packets, and it may be unsafe to use the
  484          * trailing space.
  485          */
  486         if (!(n->m_flags & M_EXT) && extendsiz < M_TRAILINGSPACE(n)) {
  487                 extend = mtod(n, u_char *) + n->m_len;
  488                 n->m_len += extendsiz;
  489                 m->m_pkthdr.len += extendsiz;
  490         } else {
  491                 struct mbuf *nn;
  492 
  493                 MGET(nn, M_DONTWAIT, MT_DATA);
  494                 if (!nn) {
  495                         ipseclog((LOG_DEBUG, "esp%d_output: can't alloc mbuf",
  496                             afnumber));
  497                         error = ENOBUFS;
  498                         goto fail;
  499                 }
  500                 extend = mtod(nn, u_char *);
  501                 nn->m_len = extendsiz;
  502                 nn->m_next = NULL;
  503                 n->m_next = nn;
  504                 n = nn;
  505                 m->m_pkthdr.len += extendsiz;
  506         }
  507         switch (sav->flags & SADB_X_EXT_PMASK) {
  508         case SADB_X_EXT_PRAND:
  509                 key_randomfill(extend, extendsiz);
  510                 break;
  511         case SADB_X_EXT_PZERO:
  512                 bzero(extend, extendsiz);
  513                 break;
  514         case SADB_X_EXT_PSEQ:
  515                 for (i = 0; i < extendsiz; i++)
  516                         extend[i] = (i + 1) & 0xff;
  517                 break;
  518         }
  519 
  520 #ifdef IPSEC_NAT_T
  521         if (sav->natt_type != 0) {
  522                 *nexthdrp = IPPROTO_UDP;
  523 
  524                 /*
  525                  * Create the UDP encapsulation header for NAT-T
  526                  * uh_len is set later, when the size is known.
  527                  */
  528                 if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE)
  529                         udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT);
  530                 else
  531                         udp->uh_sport = 
  532                             KEY_PORTFROMSADDR(&sav->sah->saidx.src);
  533 
  534                 udp->uh_dport = KEY_PORTFROMSADDR(&sav->sah->saidx.dst);
  535                 udp->uh_sum = 0;
  536         } else {
  537                 *nexthdrp = IPPROTO_ESP;
  538         }
  539 #endif
  540 
  541         /* initialize esp trailer. */
  542         esptail = (struct esptail *)
  543                 (mtod(n, u_int8_t *) + n->m_len - sizeof(struct esptail));
  544         esptail->esp_nxt = nxt;
  545         esptail->esp_padlen = extendsiz - 2;
  546 
  547         /* modify IP header (for ESP header part only) */
  548         switch (af) {
  549 #ifdef INET
  550         case AF_INET:
  551                 ip = mtod(m, struct ip *);
  552                 if (extendsiz < (IP_MAXPACKET - ntohs(ip->ip_len)))
  553                         ip->ip_len = htons(ntohs(ip->ip_len) + extendsiz);
  554                 else {
  555                         ipseclog((LOG_ERR,
  556                             "IPv4 ESP output: size exceeds limit\n"));
  557                         IPSEC_STATINC(IPSEC_STAT_OUT_INVAL);
  558                         error = EMSGSIZE;
  559                         goto fail;
  560                 }
  561                 break;
  562 #endif
  563 #ifdef INET6
  564         case AF_INET6:
  565                 /* total packet length will be computed in ip6_output() */
  566                 break;
  567 #endif
  568         }
  569     }
  570 
  571         /*
  572          * pre-compute and cache intermediate key
  573          */
  574         error = esp_schedule(algo, sav);
  575         if (error) {
  576                 _NET_STATINC(stat, IPSEC_STAT_OUT_INVAL);
  577                 goto fail;
  578         }
  579 
  580         /*
  581          * encrypt the packet, based on security association
  582          * and the algorithm specified.
  583          */
  584         if (!algo->encrypt)
  585                 panic("internal error: no encrypt function");
  586         if ((*algo->encrypt)(m, espoff, plen + extendsiz, sav, algo, ivlen)) {
  587                 /* m is already freed */
  588                 m = NULL;
  589                 ipseclog((LOG_ERR, "packet encryption failure\n"));
  590                 _NET_STATINC(stat, IPSEC_STAT_OUT_INVAL);
  591                 error = EINVAL;
  592                 goto fail;
  593         }
  594 
  595         /*
  596          * calculate ICV if required.
  597          */
  598         if (!sav->replay)
  599                 goto noantireplay;
  600         if (!sav->key_auth)
  601                 goto noantireplay;
  602         if (sav->key_auth == SADB_AALG_NONE)
  603                 goto noantireplay;
  604 
  605     {
  606         const struct ah_algorithm *aalgo;
  607         u_char authbuf[AH_MAXSUMSIZE];
  608         u_char *p;
  609         size_t siz;
  610 #ifdef INET
  611         struct ip *ip;
  612 #endif
  613 
  614         aalgo = ah_algorithm_lookup(sav->alg_auth);
  615         if (!aalgo)
  616                 goto noantireplay;
  617         siz = ((aalgo->sumsiz)(sav) + 3) & ~(4 - 1);
  618         if (AH_MAXSUMSIZE < siz)
  619                 panic("assertion failed for AH_MAXSUMSIZE");
  620 
  621         if (esp_auth(m, espoff, m->m_pkthdr.len - espoff, sav, authbuf)) {
  622                 ipseclog((LOG_ERR, "ESP checksum generation failure\n"));
  623                 error = EINVAL;
  624                 _NET_STATINC(stat, IPSEC_STAT_OUT_INVAL);
  625                 goto fail;
  626         }
  627 
  628         n = m;
  629         while (n->m_next)
  630                 n = n->m_next;
  631 
  632         if (!(n->m_flags & M_EXT) && siz < M_TRAILINGSPACE(n)) { /* XXX */
  633                 n->m_len += siz;
  634                 m->m_pkthdr.len += siz;
  635                 p = mtod(n, u_char *) + n->m_len - siz;
  636         } else {
  637                 struct mbuf *nn;
  638 
  639                 MGET(nn, M_DONTWAIT, MT_DATA);
  640                 if (!nn) {
  641                         ipseclog((LOG_DEBUG, "can't alloc mbuf in esp%d_output",
  642                             afnumber));
  643                         error = ENOBUFS;
  644                         goto fail;
  645                 }
  646                 nn->m_len = siz;
  647                 nn->m_next = NULL;
  648                 n->m_next = nn;
  649                 n = nn;
  650                 m->m_pkthdr.len += siz;
  651                 p = mtod(nn, u_char *);
  652         }
  653         bcopy(authbuf, p, siz);
  654 
  655         /* modify IP header (for ESP header part only) */
  656         switch (af) {
  657 #ifdef INET
  658         case AF_INET:
  659                 ip = mtod(m, struct ip *);
  660                 if (siz < (IP_MAXPACKET - ntohs(ip->ip_len)))
  661                         ip->ip_len = htons(ntohs(ip->ip_len) + siz);
  662                 else {
  663                         ipseclog((LOG_ERR,
  664                             "IPv4 ESP output: size exceeds limit\n"));
  665                         IPSEC_STATINC(IPSEC_STAT_OUT_INVAL);
  666                         error = EMSGSIZE;
  667                         goto fail;
  668                 }
  669                 break;
  670 #endif
  671 #ifdef INET6
  672         case AF_INET6:
  673                 /* total packet length will be computed in ip6_output() */
  674                 break;
  675 #endif
  676         }
  677     }
  678 
  679 noantireplay:
  680 #ifdef IPSEC_NAT_T
  681         if (sav->natt_type != 0) {
  682                 struct ip *ip;
  683                 ip = mtod(m, struct ip *);
  684                 udp->uh_ulen = htons(ntohs(ip->ip_len) - (ip->ip_hl << 2));
  685 
  686         }
  687 #endif /* IPSEC_NAT_T */
  688 
  689         if (!m) {
  690                 ipseclog((LOG_ERR,
  691                     "NULL mbuf after encryption in esp%d_output", afnumber));
  692         } else
  693                 _NET_STATINC(stat, IPSEC_STAT_OUT_SUCCESS);
  694         _NET_STATINC(stat, IPSEC_STAT_OUT_ESPHIST + sav->alg_enc);
  695         key_sa_recordxfer(sav, m);
  696         return 0;
  697 
  698 fail:
  699         m_freem(m);
  700 #if 1
  701         return error;
  702 #else
  703         panic("something bad in esp_output");
  704 #endif
  705 }
  706 
  707 #ifdef INET
  708 int
  709 esp4_output(struct mbuf *m, struct ipsecrequest *isr)
  710 {
  711         struct ip *ip;
  712         if (m->m_len < sizeof(struct ip)) {
  713                 ipseclog((LOG_DEBUG, "esp4_output: first mbuf too short\n"));
  714                 m_freem(m);
  715                 return EINVAL;
  716         }
  717         ip = mtod(m, struct ip *);
  718         /* XXX assumes that m->m_next points to payload */
  719         return esp_output(m, &ip->ip_p, m->m_next, isr, AF_INET);
  720 }
  721 #endif /* INET */
  722 
  723 #ifdef INET6
  724 int
  725 esp6_output(struct mbuf *m, u_char *nexthdrp, 
  726         struct mbuf *md, struct ipsecrequest *isr)
  727 {
  728         if (m->m_len < sizeof(struct ip6_hdr)) {
  729                 ipseclog((LOG_DEBUG, "esp6_output: first mbuf too short\n"));
  730                 m_freem(m);
  731                 return EINVAL;
  732         }
  733         return esp_output(m, nexthdrp, md, isr, AF_INET6);
  734 }
  735 #endif /* INET6 */

Cache object: 098b7ff98a6cbacb6c6f08340b6fa866


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