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/netinet/ip_ipcomp.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 /* $OpenBSD: ip_ipcomp.c,v 1.92 2022/05/03 09:18:11 claudio Exp $ */
    2 
    3 /*
    4  * Copyright (c) 2001 Jean-Jacques Bernard-Gundol (jj@wabbitt.org)
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  *
   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  * 3. The name of the author may not be used to endorse or promote products
   16  *   derived from this software without specific prior written permission.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   28  */
   29 
   30 /* IP payload compression protocol (IPComp), see RFC 2393 */
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/mbuf.h>
   35 #include <sys/socket.h>
   36 
   37 #include <net/if.h>
   38 #include <net/if_var.h>
   39 #include <net/bpf.h>
   40 
   41 #include <netinet/in.h>
   42 #include <netinet/ip.h>
   43 #include <netinet/ip_var.h>
   44 
   45 #ifdef INET6
   46 #include <netinet/ip6.h>
   47 #endif                          /* INET6 */
   48 
   49 #include <netinet/ip_ipsp.h>
   50 #include <netinet/ip_ipcomp.h>
   51 #include <net/pfkeyv2.h>
   52 #include <net/if_enc.h>
   53 
   54 #include <crypto/cryptodev.h>
   55 #include <crypto/xform.h>
   56 
   57 #include "bpfilter.h"
   58 
   59 #ifdef ENCDEBUG
   60 #define DPRINTF(fmt, args...)                                           \
   61         do {                                                            \
   62                 if (encdebug)                                           \
   63                         printf("%s: " fmt "\n", __func__, ## args);     \
   64         } while (0)
   65 #else
   66 #define DPRINTF(fmt, args...)                                           \
   67         do { } while (0)
   68 #endif
   69 
   70 /*
   71  * ipcomp_attach() is called from the transformation code
   72  */
   73 int
   74 ipcomp_attach(void)
   75 {
   76         return 0;
   77 }
   78 
   79 /*
   80  * ipcomp_init() is called when an CPI is being set up.
   81  */
   82 int
   83 ipcomp_init(struct tdb *tdbp, const struct xformsw *xsp, struct ipsecinit *ii)
   84 {
   85         const struct comp_algo *tcomp = NULL;
   86         struct cryptoini cric;
   87         int error;
   88 
   89         switch (ii->ii_compalg) {
   90         case SADB_X_CALG_DEFLATE:
   91                 tcomp = &comp_algo_deflate;
   92                 break;
   93         default:
   94                 DPRINTF("unsupported compression algorithm %d specified",
   95                     ii->ii_compalg);
   96                 return EINVAL;
   97         }
   98 
   99         tdbp->tdb_compalgxform = tcomp;
  100 
  101         DPRINTF("initialized TDB with ipcomp algorithm %s", tcomp->name);
  102 
  103         tdbp->tdb_xform = xsp;
  104 
  105         /* Initialize crypto session */
  106         memset(&cric, 0, sizeof(cric));
  107         cric.cri_alg = tdbp->tdb_compalgxform->type;
  108 
  109         KERNEL_LOCK();
  110         error = crypto_newsession(&tdbp->tdb_cryptoid, &cric, 0);
  111         KERNEL_UNLOCK();
  112         return error;
  113 }
  114 
  115 /*
  116  * ipcomp_zeroize() used when IPCA is deleted
  117  */
  118 int
  119 ipcomp_zeroize(struct tdb *tdbp)
  120 {
  121         int error;
  122 
  123         KERNEL_LOCK();
  124         error = crypto_freesession(tdbp->tdb_cryptoid);
  125         KERNEL_UNLOCK();
  126         tdbp->tdb_cryptoid = 0;
  127         return error;
  128 }
  129 
  130 /*
  131  * ipcomp_input() gets called to uncompress an input packet
  132  */
  133 int
  134 ipcomp_input(struct mbuf **mp, struct tdb *tdb, int skip, int protoff)
  135 {
  136         const struct comp_algo *ipcompx = tdb->tdb_compalgxform;
  137         struct mbuf *m = *mp;
  138         struct cryptodesc *crdc = NULL;
  139         struct cryptop *crp;
  140         int hlen, error, clen, roff;
  141         u_int8_t nproto;
  142         u_int64_t ibytes;
  143         struct mbuf *m1, *mo;
  144         struct ipcomp  *ipcomp;
  145         caddr_t addr;
  146 #ifdef ENCDEBUG
  147         char buf[INET6_ADDRSTRLEN];
  148 #endif
  149 
  150         hlen = IPCOMP_HLENGTH;
  151 
  152         /* Get crypto descriptors */
  153         crp = crypto_getreq(1);
  154         if (crp == NULL) {
  155                 DPRINTF("failed to acquire crypto descriptors");
  156                 ipcompstat_inc(ipcomps_crypto);
  157                 goto drop;
  158         }
  159         crdc = &crp->crp_desc[0];
  160 
  161         crdc->crd_skip = skip + hlen;
  162         crdc->crd_len = m->m_pkthdr.len - (skip + hlen);
  163         crdc->crd_inject = skip;
  164 
  165         /* Decompression operation */
  166         crdc->crd_alg = ipcompx->type;
  167 
  168         /* Crypto operation descriptor */
  169         crp->crp_ilen = m->m_pkthdr.len - (skip + hlen);
  170         crp->crp_flags = CRYPTO_F_IMBUF;
  171         crp->crp_buf = (caddr_t)m;
  172         crp->crp_sid = tdb->tdb_cryptoid;
  173 
  174         while ((error = crypto_invoke(crp)) == EAGAIN) {
  175                 /* Reset the session ID */
  176                 if (tdb->tdb_cryptoid != 0)
  177                         tdb->tdb_cryptoid = crp->crp_sid;
  178         }
  179         if (error) {
  180                 DPRINTF("crypto error %d", error);
  181                 ipsecstat_inc(ipsec_noxform);
  182                 goto drop;
  183         }
  184 
  185         clen = crp->crp_olen;
  186 
  187         /* Release the crypto descriptors */
  188         crypto_freereq(crp);
  189         crp = NULL;
  190 
  191         /* update the counters */
  192         ibytes = m->m_pkthdr.len - (skip + hlen);
  193         tdb->tdb_cur_bytes += ibytes;
  194         tdbstat_add(tdb, tdb_ibytes, ibytes);
  195         ipcompstat_add(ipcomps_ibytes, ibytes);
  196 
  197         /* Hard expiration */
  198         if ((tdb->tdb_flags & TDBF_BYTES) &&
  199             (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) {
  200                 ipsecstat_inc(ipsec_exctdb);
  201                 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
  202                 tdb_delete(tdb);
  203                 goto drop;
  204         }
  205         /* Notify on soft expiration */
  206         mtx_enter(&tdb->tdb_mtx);
  207         if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
  208             (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) {
  209                 tdb->tdb_flags &= ~TDBF_SOFT_BYTES;  /* Turn off checking */
  210                 mtx_leave(&tdb->tdb_mtx);
  211                 /* may sleep in solock() for the pfkey socket */
  212                 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
  213         } else
  214                 mtx_leave(&tdb->tdb_mtx);
  215 
  216         /* In case it's not done already, adjust the size of the mbuf chain */
  217         m->m_pkthdr.len = clen + hlen + skip;
  218 
  219         if (m->m_len < skip + hlen &&
  220             (m = *mp = m_pullup(m, skip + hlen)) == NULL) {
  221                 ipcompstat_inc(ipcomps_hdrops);
  222                 goto drop;
  223         }
  224 
  225         /* Find the beginning of the IPCOMP header */
  226         m1 = m_getptr(m, skip, &roff);
  227         if (m1 == NULL) {
  228                 DPRINTF("bad mbuf chain, IPCA %s/%08x",
  229                     ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
  230                     ntohl(tdb->tdb_spi));
  231                 ipcompstat_inc(ipcomps_hdrops);
  232                 goto drop;
  233         }
  234         /* Keep the next protocol field */
  235         addr = (caddr_t) mtod(m, struct ip *) + skip;
  236         ipcomp = (struct ipcomp *) addr;
  237         nproto = ipcomp->ipcomp_nh;
  238 
  239         /* Remove the IPCOMP header from the mbuf */
  240         if (roff == 0) {
  241                 /* The IPCOMP header is at the beginning of m1 */
  242                 m_adj(m1, hlen);
  243                 /*
  244                  * If m1 is the first mbuf, it has set M_PKTHDR and m_adj()
  245                  * has already adjusted the packet header length for us.
  246                  */
  247                 if (m1 != m)
  248                         m->m_pkthdr.len -= hlen;
  249         } else if (roff + hlen >= m1->m_len) {
  250                 int adjlen;
  251 
  252                 if (roff + hlen > m1->m_len) {
  253                         adjlen = roff + hlen - m1->m_len;
  254 
  255                         /* Adjust the next mbuf by the remainder */
  256                         m_adj(m1->m_next, adjlen);
  257 
  258                         /*
  259                          * The second mbuf is guaranteed not to have a
  260                          * pkthdr...
  261                          */
  262                         m->m_pkthdr.len -= adjlen;
  263                 }
  264                 /* Now, let's unlink the mbuf chain for a second... */
  265                 mo = m1->m_next;
  266                 m1->m_next = NULL;
  267 
  268                 /* ...and trim the end of the first part of the chain...sick */
  269                 adjlen = m1->m_len - roff;
  270                 m_adj(m1, -adjlen);
  271                 /*
  272                  * If m1 is the first mbuf, it has set M_PKTHDR and m_adj()
  273                  * has already adjusted the packet header length for us.
  274                  */
  275                 if (m1 != m)
  276                         m->m_pkthdr.len -= adjlen;
  277 
  278                 /* Finally, let's relink */
  279                 m1->m_next = mo;
  280         } else {
  281                 memmove(mtod(m1, u_char *) + roff,
  282                     mtod(m1, u_char *) + roff + hlen,
  283                     m1->m_len - (roff + hlen));
  284                 m1->m_len -= hlen;
  285                 m->m_pkthdr.len -= hlen;
  286         }
  287 
  288         /* Restore the Next Protocol field */
  289         m_copyback(m, protoff, sizeof(u_int8_t), &nproto, M_NOWAIT);
  290 
  291         /* Back to generic IPsec input processing */
  292         return ipsec_common_input_cb(mp, tdb, skip, protoff);
  293 
  294  drop:
  295         m_freemp(mp);
  296         crypto_freereq(crp);
  297         return IPPROTO_DONE;
  298 }
  299 
  300 /*
  301  * IPComp output routine, called by ipsp_process_packet()
  302  */
  303 int
  304 ipcomp_output(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
  305 {
  306         const struct comp_algo *ipcompx = tdb->tdb_compalgxform;
  307         int error, hlen, ilen, olen, rlen, roff;
  308         struct cryptodesc *crdc = NULL;
  309         struct cryptop *crp = NULL;
  310         struct mbuf *mi, *mo;
  311         struct ip *ip;
  312         u_int16_t cpi;
  313 #ifdef INET6
  314         struct ip6_hdr *ip6;
  315 #endif
  316 #ifdef ENCDEBUG
  317         char buf[INET6_ADDRSTRLEN];
  318 #endif
  319 #if NBPFILTER > 0
  320         struct ifnet *encif;
  321         struct ipcomp  *ipcomp;
  322 
  323         if ((encif = enc_getif(0, tdb->tdb_tap)) != NULL) {
  324                 encif->if_opackets++;
  325                 encif->if_obytes += m->m_pkthdr.len;
  326 
  327                 if (encif->if_bpf) {
  328                         struct enchdr hdr;
  329 
  330                         memset(&hdr, 0, sizeof(hdr));
  331 
  332                         hdr.af = tdb->tdb_dst.sa.sa_family;
  333                         hdr.spi = tdb->tdb_spi;
  334 
  335                         bpf_mtap_hdr(encif->if_bpf, (char *)&hdr,
  336                             ENC_HDRLEN, m, BPF_DIRECTION_OUT);
  337                 }
  338         }
  339 #endif
  340         hlen = IPCOMP_HLENGTH;
  341 
  342         ipcompstat_inc(ipcomps_output);
  343 
  344         switch (tdb->tdb_dst.sa.sa_family) {
  345         case AF_INET:
  346                 /* Check for IPv4 maximum packet size violations */
  347                 /*
  348                  * Since compression is going to reduce the size, no need to
  349                  * worry
  350                  */
  351                 if (m->m_pkthdr.len + hlen > IP_MAXPACKET) {
  352                         DPRINTF("packet in IPCA %s/%08x got too big",
  353                             ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
  354                             ntohl(tdb->tdb_spi));
  355                         ipcompstat_inc(ipcomps_toobig);
  356                         error = EMSGSIZE;
  357                         goto drop;
  358                 }
  359                 break;
  360 
  361 #ifdef INET6
  362         case AF_INET6:
  363                 /* Check for IPv6 maximum packet size violations */
  364                 if (m->m_pkthdr.len + hlen > IPV6_MAXPACKET) {
  365                         DPRINTF("packet in IPCA %s/%08x got too big",
  366                             ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
  367                             ntohl(tdb->tdb_spi));
  368                         ipcompstat_inc(ipcomps_toobig);
  369                         error = EMSGSIZE;
  370                         goto drop;
  371                 }
  372                 break;
  373 #endif /* INET6 */
  374 
  375         default:
  376                 DPRINTF("unknown/unsupported protocol family %d, IPCA %s/%08x",
  377                     tdb->tdb_dst.sa.sa_family,
  378                     ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
  379                     ntohl(tdb->tdb_spi));
  380                 ipcompstat_inc(ipcomps_nopf);
  381                 error = EPFNOSUPPORT;
  382                 goto drop;
  383         }
  384 
  385         /* Update the counters */
  386         tdb->tdb_cur_bytes += m->m_pkthdr.len - skip;
  387         ipcompstat_add(ipcomps_obytes, m->m_pkthdr.len - skip);
  388 
  389         /* Hard byte expiration */
  390         if ((tdb->tdb_flags & TDBF_BYTES) &&
  391             (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) {
  392                 ipsecstat_inc(ipsec_exctdb);
  393                 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
  394                 tdb_delete(tdb);
  395                 error = EINVAL;
  396                 goto drop;
  397         }
  398 
  399         /* Soft byte expiration */
  400         mtx_enter(&tdb->tdb_mtx);
  401         if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
  402             (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) {
  403                 tdb->tdb_flags &= ~TDBF_SOFT_BYTES;  /* Turn off checking */
  404                 mtx_leave(&tdb->tdb_mtx);
  405                 /* may sleep in solock() for the pfkey socket */
  406                 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
  407         } else
  408                 mtx_leave(&tdb->tdb_mtx);
  409 
  410         /*
  411          * Loop through mbuf chain; if we find a readonly mbuf,
  412          * copy the packet.
  413          */
  414         mi = m;
  415         while (mi != NULL && !M_READONLY(mi))
  416                 mi = mi->m_next;
  417 
  418         if (mi != NULL) {
  419                 struct mbuf *n = m_dup_pkt(m, 0, M_DONTWAIT);
  420 
  421                 if (n == NULL) {
  422                         DPRINTF("bad mbuf chain, IPCA %s/%08x",
  423                             ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
  424                             ntohl(tdb->tdb_spi));
  425                         ipcompstat_inc(ipcomps_hdrops);
  426                         error = ENOBUFS;
  427                         goto drop;
  428                 }
  429 
  430                 m_freem(m);
  431                 m = n;
  432         }
  433         /* Ok now, we can pass to the crypto processing */
  434 
  435         /* Get crypto descriptors */
  436         crp = crypto_getreq(1);
  437         if (crp == NULL) {
  438                 DPRINTF("failed to acquire crypto descriptors");
  439                 ipcompstat_inc(ipcomps_crypto);
  440                 error = ENOBUFS;
  441                 goto drop;
  442         }
  443         crdc = &crp->crp_desc[0];
  444 
  445         /* Compression descriptor */
  446         crdc->crd_skip = skip;
  447         crdc->crd_len = m->m_pkthdr.len - skip;
  448         crdc->crd_flags = CRD_F_COMP;
  449         crdc->crd_inject = skip;
  450 
  451         /* Compression operation */
  452         crdc->crd_alg = ipcompx->type;
  453 
  454         /* Crypto operation descriptor */
  455         crp->crp_ilen = m->m_pkthdr.len;        /* Total input length */
  456         crp->crp_flags = CRYPTO_F_IMBUF;
  457         crp->crp_buf = (caddr_t)m;
  458         crp->crp_sid = tdb->tdb_cryptoid;
  459 
  460         while ((error = crypto_invoke(crp)) == EAGAIN) {
  461                 /* Reset the session ID */
  462                 if (tdb->tdb_cryptoid != 0)
  463                         tdb->tdb_cryptoid = crp->crp_sid;
  464         }
  465         if (error) {
  466                 DPRINTF("crypto error %d", error);
  467                 ipsecstat_inc(ipsec_noxform);
  468                 goto drop;
  469         }
  470 
  471         ilen = crp->crp_ilen;
  472         olen = crp->crp_olen;
  473 
  474         /* Release the crypto descriptors */
  475         crypto_freereq(crp);
  476         crp = NULL;
  477 
  478         rlen = ilen - skip;
  479 
  480         /* Check sizes. */
  481         if (rlen <= olen + IPCOMP_HLENGTH) {
  482                 /* Compression was useless, we have lost time. */
  483                 ipcompstat_inc(ipcomps_minlen); /* misnomer, but like to count */
  484                 goto skiphdr;
  485         }
  486 
  487         /* Inject IPCOMP header */
  488         mo = m_makespace(m, skip, IPCOMP_HLENGTH, &roff);
  489         if (mo == NULL) {
  490                 DPRINTF("ailed to inject IPCOMP header for IPCA %s/%08x",
  491                     ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
  492                     ntohl(tdb->tdb_spi));
  493                 ipcompstat_inc(ipcomps_wrap);
  494                 error = ENOBUFS;
  495                 goto drop;
  496         }
  497 
  498         /* Initialize the IPCOMP header */
  499         ipcomp = (struct ipcomp *)(mtod(mo, caddr_t) + roff);
  500         memset(ipcomp, 0, sizeof(struct ipcomp));
  501         cpi = (u_int16_t) ntohl(tdb->tdb_spi);
  502         ipcomp->ipcomp_cpi = htons(cpi);
  503 
  504         /* m_pullup before ? */
  505         switch (tdb->tdb_dst.sa.sa_family) {
  506         case AF_INET:
  507                 ip = mtod(m, struct ip *);
  508                 ipcomp->ipcomp_nh = ip->ip_p;
  509                 ip->ip_p = IPPROTO_IPCOMP;
  510                 break;
  511 #ifdef INET6
  512         case AF_INET6:
  513                 ip6 = mtod(m, struct ip6_hdr *);
  514                 ipcomp->ipcomp_nh = ip6->ip6_nxt;
  515                 ip6->ip6_nxt = IPPROTO_IPCOMP;
  516                 break;
  517 #endif
  518         default:
  519                 DPRINTF("unsupported protocol family %d, IPCA %s/%08x",
  520                     tdb->tdb_dst.sa.sa_family,
  521                     ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
  522                     ntohl(tdb->tdb_spi));
  523                 ipcompstat_inc(ipcomps_nopf);
  524                 error = EPFNOSUPPORT;
  525                 goto drop;
  526         }
  527 
  528  skiphdr:
  529         error = ipsp_process_done(m, tdb);
  530         if (error)
  531                 ipcompstat_inc(ipcomps_outfail);
  532         return error;
  533 
  534  drop:
  535         m_freem(m);
  536         crypto_freereq(crp);
  537         return error;
  538 }

Cache object: b1d69c71fbf6a907c04adf495b4156f6


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