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/netipsec/ipsec_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: ipsec_output.c,v 1.28 2008/04/28 17:40:11 degroote Exp $       */
    2 
    3 /*-
    4  * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  *
   28  * $FreeBSD: /repoman/r/ncvs/src/sys/netipsec/ipsec_output.c,v 1.3.2.2 2003/03/28 20:32:53 sam Exp $
   29  */
   30 
   31 #include <sys/cdefs.h>
   32 __KERNEL_RCSID(0, "$NetBSD: ipsec_output.c,v 1.28 2008/04/28 17:40:11 degroote Exp $");
   33 
   34 /*
   35  * IPsec output processing.
   36  */
   37 #include "opt_inet.h"
   38 #ifdef __FreeBSD__
   39 #include "opt_inet6.h"
   40 #endif
   41 #include "opt_ipsec.h"
   42 
   43 #include <sys/param.h>
   44 #include <sys/systm.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/syslog.h>
   51 
   52 #include <net/if.h>
   53 #include <net/route.h>
   54 
   55 #include <netinet/in.h>
   56 #include <netinet/in_systm.h>
   57 #include <netinet/ip.h>
   58 #include <netinet/ip_var.h>
   59 #include <netinet/in_var.h>
   60 #include <netinet/ip_ecn.h>
   61 #ifdef INET6
   62 #  ifdef __FreeBSD__
   63 #  include <netinet6/ip6_ecn.h>
   64 #  endif
   65 #endif
   66 
   67 #include <netinet/ip6.h>
   68 #ifdef INET6
   69 #include <netinet6/ip6_var.h>
   70 #endif
   71 #include <netinet/in_pcb.h>
   72 #ifdef INET6
   73 #include <netinet/icmp6.h>
   74 #endif
   75 #ifdef IPSEC_NAT_T
   76 #include <netinet/udp.h>
   77 #endif
   78 
   79 #include <netipsec/ipsec.h>
   80 #include <netipsec/ipsec_var.h>
   81 #include <netipsec/ipsec_private.h>
   82 #ifdef INET6
   83 #include <netipsec/ipsec6.h>
   84 #endif
   85 #include <netipsec/ah_var.h>
   86 #include <netipsec/esp_var.h>
   87 #include <netipsec/ipcomp_var.h>
   88 
   89 #include <netipsec/xform.h>
   90 
   91 #include <netipsec/key.h>
   92 #include <netipsec/keydb.h>
   93 #include <netipsec/key_debug.h>
   94 #include <netipsec/ipsec_osdep.h>
   95 
   96 #include <net/net_osdep.h>              /* ovbcopy() in ipsec6_encapsulate() */
   97 
   98 
   99 /*
  100  * Add a IPSEC_OUT_DONE tag to mark that we have finished the ipsec processing
  101  * It will be used by ip{,6}_output to check if we have already or not 
  102  * processed this packet.
  103  */
  104 static int
  105 ipsec_register_done(struct mbuf *m, int * error)
  106 {
  107         struct m_tag *mtag;
  108 
  109         mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, 0, M_NOWAIT);
  110         if (mtag == NULL) {
  111                 DPRINTF(("ipsec_register_done: could not get packet tag\n"));
  112                 *error = ENOMEM;
  113                 return -1;
  114         }
  115 
  116         m_tag_prepend(m, mtag);
  117         return 0;
  118 }
  119 
  120 static int
  121 ipsec_reinject_ipstack(struct mbuf *m, int af)
  122 {
  123 #ifdef INET
  124         struct ip * ip;
  125 #endif /* INET */
  126 
  127         switch (af) {
  128 #ifdef INET
  129         case AF_INET:
  130                 ip = mtod(m, struct ip *);
  131 #ifdef __FreeBSD__
  132                 /* FreeBSD ip_output() expects ip_len, ip_off in host endian */
  133                 ip->ip_len = ntohs(ip->ip_len);
  134                 ip->ip_off = ntohs(ip->ip_off);
  135 #endif /* __FreeBSD_ */
  136                 return ip_output(m, NULL, NULL, IP_RAWOUTPUT,
  137                     (struct ip_moptions *)NULL, (struct socket *)NULL);
  138 
  139 #endif /* INET */
  140 #ifdef INET6
  141         case AF_INET6:
  142                 /*
  143                  * We don't need massage, IPv6 header fields are always in
  144                  * net endian.
  145                  */
  146                 return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL);
  147 #endif /* INET6 */
  148         }
  149 
  150         panic("ipsec_reinject_ipstack : iunknown protocol family %u\n", af);
  151         return -1; /* NOTREACHED */
  152 }
  153 
  154 int
  155 ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
  156 {
  157         struct secasvar *sav;
  158         struct secasindex *saidx;
  159         int error;
  160 #ifdef INET
  161         struct ip * ip;
  162 #endif /* INET */
  163 #ifdef INET6
  164         struct ip6_hdr * ip6;
  165 #endif /* INET6 */
  166 #ifdef IPSEC_NAT_T
  167         struct mbuf * mo;
  168         struct udphdr *udp = NULL;
  169         uint64_t * data = NULL;
  170         int hlen, roff;
  171 #endif /* IPSEC_NAT_T */
  172 
  173         IPSEC_SPLASSERT_SOFTNET("ipsec_process_done");
  174 
  175         IPSEC_ASSERT(m != NULL, ("ipsec_process_done: null mbuf"));
  176         IPSEC_ASSERT(isr != NULL, ("ipsec_process_done: null ISR"));
  177         sav = isr->sav;
  178         IPSEC_ASSERT(sav != NULL, ("ipsec_process_done: null SA"));
  179         IPSEC_ASSERT(sav->sah != NULL, ("ipsec_process_done: null SAH"));
  180 
  181         saidx = &sav->sah->saidx;
  182 
  183 #ifdef IPSEC_NAT_T
  184         if(sav->natt_type != 0) {
  185                 ip = mtod(m, struct ip *);
  186 
  187                 hlen = sizeof(struct udphdr);
  188                 if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) 
  189                         hlen += sizeof(uint64_t);
  190 
  191                 mo = m_makespace(m, sizeof(struct ip), hlen, &roff);
  192                 if (mo == NULL) {
  193                         DPRINTF(("ipsec_process_done : failed to inject" 
  194                                  "%u byte UDP for SA %s/%08lx\n",
  195                                          hlen, ipsec_address(&saidx->dst),
  196                                          (u_long) ntohl(sav->spi)));
  197                         error = ENOBUFS;
  198                         goto bad;
  199                 }
  200                 
  201                 udp = (struct udphdr*) (mtod(mo, char*) + roff);
  202                 data = (uint64_t*) (udp + 1);
  203 
  204                 if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE)
  205                         *data = 0; /* NON-IKE Marker */
  206 
  207                 if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE)
  208                         udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT);
  209                 else
  210                         udp->uh_sport = key_portfromsaddr(&saidx->src);
  211                 
  212                 udp->uh_dport = key_portfromsaddr(&saidx->dst);
  213                 udp->uh_sum = 0;
  214         udp->uh_ulen = htons(m->m_pkthdr.len - (ip->ip_hl << 2));
  215         }
  216 #endif /* IPSEC_NAT_T */
  217         
  218         switch (saidx->dst.sa.sa_family) {
  219 #ifdef INET
  220         case AF_INET:
  221                 /* Fix the header length, for AH processing. */
  222                 ip = mtod(m, struct ip *);
  223                 ip->ip_len = htons(m->m_pkthdr.len);
  224 #ifdef IPSEC_NAT_T
  225                 if (sav->natt_type != 0)
  226                         ip->ip_p = IPPROTO_UDP;
  227 #endif /* IPSEC_NAT_T */
  228                 break;
  229 #endif /* INET */
  230 #ifdef INET6
  231         case AF_INET6:
  232                 /* Fix the header length, for AH processing. */
  233                 if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) {
  234                         error = ENXIO;
  235                         goto bad;
  236                 }
  237                 if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) {
  238                         /* No jumbogram support. */
  239                         error = ENXIO;  /*?*/
  240                         goto bad;
  241                 }
  242                 ip6 = mtod(m, struct ip6_hdr *);
  243                 ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
  244 #ifdef IPSEC_NAT_T
  245                 if (sav->natt_type != 0)
  246                         ip6->ip6_nxt = IPPROTO_UDP;
  247 #endif /* IPSEC_NAT_T */
  248                 break;
  249 #endif /* INET6 */
  250         default:
  251                 DPRINTF(("ipsec_process_done: unknown protocol family %u\n",
  252                     saidx->dst.sa.sa_family));
  253                 error = ENXIO;
  254                 goto bad;
  255         }
  256 
  257         /*
  258          * If there's another (bundled) SA to apply, do so.
  259          * Note that this puts a burden on the kernel stack size.
  260          * If this is a problem we'll need to introduce a queue
  261          * to set the packet on so we can unwind the stack before
  262          * doing further processing.
  263          */
  264         if (isr->next) {
  265                 IPSEC_STATINC(IPSEC_STAT_OUT_BUNDLESA);
  266         switch ( saidx->dst.sa.sa_family ) {
  267 #ifdef INET
  268         case AF_INET:
  269                         return ipsec4_process_packet(m, isr->next, 0,0);
  270 #endif /* INET */
  271 #ifdef INET6
  272                 case AF_INET6:
  273                 return ipsec6_process_packet(m,isr->next);
  274 #endif /* INET6 */
  275                 default :
  276                         DPRINTF(("ipsec_process_done: unknown protocol family %u\n",
  277                                saidx->dst.sa.sa_family));
  278                         error = ENXIO;
  279                         goto bad;
  280         }
  281         }
  282 
  283         /*
  284          * We're done with IPsec processing, 
  285          * mark that we have already processed the packet
  286          * transmit it packet using the appropriate network protocol (IP or IPv6). 
  287          */
  288 
  289         if (ipsec_register_done(m, &error) < 0)
  290                 goto bad;
  291 
  292         return ipsec_reinject_ipstack(m, saidx->dst.sa.sa_family);
  293 bad:
  294         m_freem(m);
  295         KEY_FREESAV(&sav);
  296         return (error);
  297 }
  298 
  299 /*
  300  * ipsec_nextisr can return :
  301  * - isr == NULL and error != 0 => something is bad : the packet must be
  302  *   discarded
  303  * - isr == NULL and error == 0 => no more rules to apply, ipsec processing 
  304  *   is done, reinject it in ip stack
  305  * - isr != NULL (error == 0) => we need to apply one rule to the packet
  306  */
  307 static struct ipsecrequest *
  308 ipsec_nextisr(
  309         struct mbuf *m,
  310         struct ipsecrequest *isr,
  311         int af,
  312         struct secasindex *saidx,
  313         int *error
  314 )
  315 {
  316 #define IPSEC_OSTAT(x, y, z)                                            \
  317 do {                                                                    \
  318         switch (isr->saidx.proto) {                                     \
  319         case IPPROTO_ESP:                                               \
  320                 ESP_STATINC(x);                                         \
  321                 break;                                                  \
  322         case IPPROTO_AH:                                                \
  323                 AH_STATINC(y);                                          \
  324                 break;                                                  \
  325         default:                                                        \
  326                 IPCOMP_STATINC(z);                                      \
  327                 break;                                                  \
  328         }                                                               \
  329 } while (/*CONSTCOND*/0)
  330 
  331         struct secasvar *sav;
  332 
  333         IPSEC_SPLASSERT_SOFTNET("ipsec_nextisr");
  334         IPSEC_ASSERT(af == AF_INET || af == AF_INET6,
  335                 ("ipsec_nextisr: invalid address family %u", af));
  336 again:
  337         /*
  338          * Craft SA index to search for proper SA.  Note that
  339          * we only fillin unspecified SA peers for transport
  340          * mode; for tunnel mode they must already be filled in.
  341          */
  342         *saidx = isr->saidx;
  343         if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
  344                 /* Fillin unspecified SA peers only for transport mode */
  345                 if (af == AF_INET) {
  346                         struct sockaddr_in *sin;
  347                         struct ip *ip = mtod(m, struct ip *);
  348 
  349                         if (saidx->src.sa.sa_len == 0) {
  350                                 sin = &saidx->src.sin;
  351                                 sin->sin_len = sizeof(*sin);
  352                                 sin->sin_family = AF_INET;
  353                                 sin->sin_port = IPSEC_PORT_ANY;
  354                                 sin->sin_addr = ip->ip_src;
  355                         }
  356                         if (saidx->dst.sa.sa_len == 0) {
  357                                 sin = &saidx->dst.sin;
  358                                 sin->sin_len = sizeof(*sin);
  359                                 sin->sin_family = AF_INET;
  360                                 sin->sin_port = IPSEC_PORT_ANY;
  361                                 sin->sin_addr = ip->ip_dst;
  362                         }
  363                 } else {
  364                         struct sockaddr_in6 *sin6;
  365                         struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
  366 
  367                         if (saidx->src.sin6.sin6_len == 0) {
  368                                 sin6 = (struct sockaddr_in6 *)&saidx->src;
  369                                 sin6->sin6_len = sizeof(*sin6);
  370                                 sin6->sin6_family = AF_INET6;
  371                                 sin6->sin6_port = IPSEC_PORT_ANY;
  372                                 sin6->sin6_addr = ip6->ip6_src;
  373                                 if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
  374                                         /* fix scope id for comparing SPD */
  375                                         sin6->sin6_addr.s6_addr16[1] = 0;
  376                                         sin6->sin6_scope_id =
  377                                             ntohs(ip6->ip6_src.s6_addr16[1]);
  378                                 }
  379                         }
  380                         if (saidx->dst.sin6.sin6_len == 0) {
  381                                 sin6 = (struct sockaddr_in6 *)&saidx->dst;
  382                                 sin6->sin6_len = sizeof(*sin6);
  383                                 sin6->sin6_family = AF_INET6;
  384                                 sin6->sin6_port = IPSEC_PORT_ANY;
  385                                 sin6->sin6_addr = ip6->ip6_dst;
  386                                 if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
  387                                         /* fix scope id for comparing SPD */
  388                                         sin6->sin6_addr.s6_addr16[1] = 0;
  389                                         sin6->sin6_scope_id =
  390                                             ntohs(ip6->ip6_dst.s6_addr16[1]);
  391                                 }
  392                         }
  393                 }
  394         }
  395 
  396         /*
  397          * Lookup SA and validate it.
  398          */
  399         *error = key_checkrequest(isr, saidx);
  400         if (*error != 0) {
  401                 /*
  402                  * IPsec processing is required, but no SA found.
  403                  * I assume that key_acquire() had been called
  404                  * to get/establish the SA. Here I discard
  405                  * this packet because it is responsibility for
  406                  * upper layer to retransmit the packet.
  407                  */
  408                 IPSEC_STATINC(IPSEC_STAT_OUT_NOSA);
  409                 goto bad;
  410         }
  411         sav = isr->sav;
  412         /* sav may be NULL here if we have an USE rule */
  413         if (sav == NULL) {              
  414                 IPSEC_ASSERT(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE,
  415                         ("ipsec_nextisr: no SA found, but required; level %u",
  416                         ipsec_get_reqlevel(isr)));
  417                 isr = isr->next;
  418                 /* 
  419                  * No more rules to apply, return NULL isr and no error 
  420                  * It can happen when the last rules are USE rules
  421                  * */
  422                 if (isr == NULL) {
  423                         *error = 0;             
  424                         return isr;
  425                 }
  426                 goto again;
  427         }
  428 
  429         /*
  430          * Check system global policy controls.
  431          */
  432         if ((isr->saidx.proto == IPPROTO_ESP && !esp_enable) ||
  433             (isr->saidx.proto == IPPROTO_AH && !ah_enable) ||
  434             (isr->saidx.proto == IPPROTO_IPCOMP && !ipcomp_enable)) {
  435                 DPRINTF(("ipsec_nextisr: IPsec outbound packet dropped due"
  436                         " to policy (check your sysctls)\n"));
  437                 IPSEC_OSTAT(ESP_STAT_PDROPS, AH_STAT_PDROPS,
  438                     IPCOMP_STAT_PDROPS);
  439                 *error = EHOSTUNREACH;
  440                 goto bad;
  441         }
  442 
  443         /*
  444          * Sanity check the SA contents for the caller
  445          * before they invoke the xform output method.
  446          */
  447         if (sav->tdb_xform == NULL) {
  448                 DPRINTF(("ipsec_nextisr: no transform for SA\n"));
  449                 IPSEC_OSTAT(ESP_STAT_NOXFORM, AH_STAT_NOXFORM,
  450                     IPCOMP_STAT_NOXFORM);
  451                 *error = EHOSTUNREACH;
  452                 goto bad;
  453         }
  454         return isr;
  455 bad:
  456         IPSEC_ASSERT(*error != 0, ("ipsec_nextisr: error return w/ no error code"));
  457         return NULL;
  458 #undef IPSEC_OSTAT
  459 }
  460 
  461 #ifdef INET
  462 /*
  463  * IPsec output logic for IPv4.
  464  */
  465 int
  466 ipsec4_process_packet(
  467     struct mbuf *m,
  468     struct ipsecrequest *isr,
  469     int flags,
  470     int tunalready
  471 )
  472 {
  473         struct secasindex saidx;
  474         struct secasvar *sav;
  475         struct ip *ip;
  476         int s, error, i, off;
  477 
  478         IPSEC_ASSERT(m != NULL, ("ipsec4_process_packet: null mbuf"));
  479         IPSEC_ASSERT(isr != NULL, ("ipsec4_process_packet: null isr"));
  480 
  481         s = splsoftnet();                       /* insure SA contents don't change */
  482 
  483         isr = ipsec_nextisr(m, isr, AF_INET, &saidx, &error);
  484         if (isr == NULL) {
  485                 if (error != 0) {
  486                         goto bad;
  487                 } else {
  488                         if (ipsec_register_done(m, &error) < 0)
  489                                 goto bad;
  490 
  491                         splx(s);
  492                         return ipsec_reinject_ipstack(m, AF_INET);
  493                 }
  494         }
  495 
  496         sav = isr->sav;
  497         if (!tunalready) {
  498                 union sockaddr_union *dst = &sav->sah->saidx.dst;
  499                 int setdf;
  500 
  501                 /*
  502                  * Collect IP_DF state from the outer header.
  503                  */
  504                 if (dst->sa.sa_family == AF_INET) {
  505                         if (m->m_len < sizeof (struct ip) &&
  506                             (m = m_pullup(m, sizeof (struct ip))) == NULL) {
  507                                 error = ENOBUFS;
  508                                 goto bad;
  509                         }
  510                         ip = mtod(m, struct ip *);
  511                         /* Honor system-wide control of how to handle IP_DF */
  512                         switch (ip4_ipsec_dfbit) {
  513                         case 0:                 /* clear in outer header */
  514                         case 1:                 /* set in outer header */
  515                                 setdf = ip4_ipsec_dfbit;
  516                                 break;
  517                         default:                /* propagate to outer header */
  518                                 setdf = ip->ip_off;
  519 #ifndef __FreeBSD__
  520                 /* On FreeBSD, ip_off and ip_len assumed in host endian. */
  521                                 setdf = ntohs(setdf);
  522 #endif
  523                                 setdf = htons(setdf & IP_DF);
  524                                 break;
  525                         }
  526                 } else {
  527                         ip = NULL;              /* keep compiler happy */
  528                         setdf = 0;
  529                 }
  530                 /* Do the appropriate encapsulation, if necessary */
  531                 if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
  532                     dst->sa.sa_family != AF_INET ||         /* PF mismatch */
  533 #if 0
  534                     (sav->flags & SADB_X_SAFLAGS_TUNNEL) || /* Tunnel requ'd */
  535                     sav->tdb_xform->xf_type == XF_IP4 ||    /* ditto */
  536 #endif
  537                     (dst->sa.sa_family == AF_INET &&        /* Proxy */
  538                      dst->sin.sin_addr.s_addr != INADDR_ANY &&
  539                      dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) {
  540                         struct mbuf *mp;
  541 
  542                         /* Fix IPv4 header checksum and length */
  543                         if (m->m_len < sizeof (struct ip) &&
  544                             (m = m_pullup(m, sizeof (struct ip))) == NULL) {
  545                                 error = ENOBUFS;
  546                                 goto bad;
  547                         }
  548                         ip = mtod(m, struct ip *);
  549                         ip->ip_len = htons(m->m_pkthdr.len);
  550                         ip->ip_sum = 0;
  551                         ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
  552 
  553                         /* Encapsulate the packet */
  554                         error = ipip_output(m, isr, &mp, 0, 0);
  555                         if (mp == NULL && !error) {
  556                                 /* Should never happen. */
  557                                 DPRINTF(("ipsec4_process_packet: ipip_output "
  558                                         "returns no mbuf and no error!"));
  559                                 error = EFAULT;
  560                         }
  561                         if (error) {
  562                                 if (mp) {
  563                                         /* XXX: Should never happen! */
  564                                         m_freem(mp);
  565                                 }
  566                                 m = NULL; /* ipip_output() already freed it */
  567                                 goto bad;
  568                         }
  569                         m = mp, mp = NULL;
  570                         /*
  571                          * ipip_output clears IP_DF in the new header.  If
  572                          * we need to propagate IP_DF from the outer header,
  573                          * then we have to do it here.
  574                          *
  575                          * XXX shouldn't assume what ipip_output does.
  576                          */
  577                         if (dst->sa.sa_family == AF_INET && setdf) {
  578                                 if (m->m_len < sizeof (struct ip) &&
  579                                     (m = m_pullup(m, sizeof (struct ip))) == NULL) {
  580                                         error = ENOBUFS;
  581                                         goto bad;
  582                                 }
  583                                 ip = mtod(m, struct ip *);
  584                                 ip->ip_off |= IP_OFF_CONVERT(IP_DF);
  585                         }
  586                 }
  587         }
  588 
  589         /*
  590          * Dispatch to the appropriate IPsec transform logic.  The
  591          * packet will be returned for transmission after crypto
  592          * processing, etc. are completed.  For encapsulation we
  593          * bypass this call because of the explicit call done above
  594          * (necessary to deal with IP_DF handling for IPv4).
  595          *
  596          * NB: m & sav are ``passed to caller'' who's reponsible for
  597          *     for reclaiming their resources.
  598          */
  599         if (sav->tdb_xform->xf_type != XF_IP4) {
  600                 ip = mtod(m, struct ip *);
  601                 i = ip->ip_hl << 2;
  602                 off = offsetof(struct ip, ip_p);
  603                 error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off);
  604         } else {
  605                 error = ipsec_process_done(m, isr);
  606         }
  607         splx(s);
  608         return error;
  609 bad:
  610         splx(s);
  611         if (m)
  612                 m_freem(m);
  613         return error;
  614 }
  615 #endif
  616 
  617 #ifdef INET6
  618 int
  619 ipsec6_process_packet(
  620         struct mbuf *m,
  621         struct ipsecrequest *isr
  622     )
  623 {
  624         struct secasindex saidx;
  625         struct secasvar *sav;
  626         struct ip6_hdr *ip6;
  627         int s, error, i, off; 
  628 
  629         IPSEC_ASSERT(m != NULL, ("ipsec6_process_packet: null mbuf"));
  630         IPSEC_ASSERT(isr != NULL, ("ipsec6_process_packet: null isr"));
  631 
  632         s = splsoftnet();   /* insure SA contents don't change */
  633         isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error);
  634         if (isr == NULL) {
  635                 if (error != 0) {
  636                         // XXX Should we send a notification ?
  637                         goto bad;
  638                 } else {
  639                         if (ipsec_register_done(m, &error) < 0)
  640                                 goto bad;
  641 
  642                         splx(s);
  643                         return ipsec_reinject_ipstack(m, AF_INET6);
  644                 }
  645         }
  646 
  647         sav = isr->sav;
  648         if (sav->tdb_xform->xf_type != XF_IP4) {
  649                 i = sizeof(struct ip6_hdr);
  650                 off = offsetof(struct ip6_hdr, ip6_nxt);
  651                 error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off);
  652        } else {
  653                 union sockaddr_union *dst = &sav->sah->saidx.dst;
  654 
  655         ip6 = mtod(m, struct ip6_hdr *);
  656 
  657                 /* Do the appropriate encapsulation, if necessary */
  658                 if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
  659                dst->sa.sa_family != AF_INET6 ||        /* PF mismatch */
  660             ((dst->sa.sa_family == AF_INET6) &&
  661                 (!IN6_IS_ADDR_UNSPECIFIED(&dst->sin6.sin6_addr)) &&
  662                 (!IN6_ARE_ADDR_EQUAL(&dst->sin6.sin6_addr,
  663                 &ip6->ip6_dst))) 
  664             )
  665                 {
  666                         struct mbuf *mp;
  667             /* Fix IPv6 header payload length. */
  668             if (m->m_len < sizeof(struct ip6_hdr))
  669                 if ((m = m_pullup(m,sizeof(struct ip6_hdr))) == NULL)
  670                    return ENOBUFS;
  671 
  672             if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) {
  673                 /* No jumbogram support. */
  674                 m_freem(m);
  675                 return ENXIO;   /*XXX*/
  676             }
  677             ip6 = mtod(m, struct ip6_hdr *);
  678             ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6));
  679 
  680                         /* Encapsulate the packet */
  681                         error = ipip_output(m, isr, &mp, 0, 0);
  682                         if (mp == NULL && !error) {
  683                                 /* Should never happen. */
  684                                 DPRINTF(("ipsec6_process_packet: ipip_output "
  685                                                  "returns no mbuf and no error!"));
  686                                  error = EFAULT;
  687                         }
  688 
  689                         if (error) {
  690                                 if (mp) {
  691                                         /* XXX: Should never happen! */
  692                                         m_freem(mp);
  693                                 }
  694                                 m = NULL; /* ipip_output() already freed it */
  695                                 goto bad;
  696                         }
  697     
  698                         m = mp;
  699                         mp = NULL;
  700                 }
  701         
  702                 error = ipsec_process_done(m,isr);
  703                 }
  704                 splx(s);
  705                 return error;
  706 bad:
  707         splx(s);
  708         if (m)
  709                 m_freem(m);
  710         return error;
  711 }
  712 #endif /*INET6*/

Cache object: a919f416899790f7e0028ba7189282e8


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