1 /* $FreeBSD: src/sys/netipsec/xform_ipcomp.c,v 1.1.4.2 2003/02/26 00:14:06 sam Exp $ */
2 /* $OpenBSD: ip_ipcomp.c,v 1.1 2001/07/05 12:08:52 jjbg Exp $ */
5 * Copyright (c) 2001 Jean-Jacques Bernard-Gundol (jj@wabbitt.org)
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 /* IP payload compression protocol (IPComp), see RFC 2393 */
33 #include "opt_inet6.h"
35 #include <sys/param.h>
36 #include <sys/systm.h>
38 #include <sys/socket.h>
39 #include <sys/kernel.h>
40 #include <sys/protosw.h>
41 #include <sys/sysctl.h>
42 #include <sys/thread2.h>
44 #include <netinet/in.h>
45 #include <netinet/in_systm.h>
46 #include <netinet/ip.h>
47 #include <netinet/ip_var.h>
49 #include <net/route.h>
50 #include <netproto/ipsec/ipsec.h>
51 #include <netproto/ipsec/xform.h>
54 #include <netinet/ip6.h>
55 #include <netproto/ipsec/ipsec6.h>
58 #include <netproto/ipsec/ipcomp.h>
59 #include <netproto/ipsec/ipcomp_var.h>
61 #include <netproto/ipsec/key.h>
62 #include <netproto/ipsec/key_debug.h>
64 #include <opencrypto/cryptodev.h>
65 #include <opencrypto/deflate.h>
66 #include <opencrypto/xform.h>
68 int ipcomp_enable
= 0;
69 struct ipcompstat ipcompstat
;
71 SYSCTL_DECL(_net_inet_ipcomp
);
72 SYSCTL_INT(_net_inet_ipcomp
, OID_AUTO
,
73 ipcomp_enable
, CTLFLAG_RW
, &ipcomp_enable
, 0, "");
74 SYSCTL_STRUCT(_net_inet_ipcomp
, IPSECCTL_STATS
,
75 stats
, CTLFLAG_RD
, &ipcompstat
, ipcompstat
, "");
77 static int ipcomp_input_cb(struct cryptop
*crp
);
78 static int ipcomp_output_cb(struct cryptop
*crp
);
81 ipcomp_algorithm_lookup(int alg
)
83 if (alg
>= IPCOMP_ALG_MAX
)
86 case SADB_X_CALG_DEFLATE
:
87 return &comp_algo_deflate
;
93 * ipcomp_init() is called when an CPI is being set up.
96 ipcomp_init(struct secasvar
*sav
, struct xformsw
*xsp
)
98 struct comp_algo
*tcomp
;
99 struct cryptoini cric
;
101 /* NB: algorithm really comes in alg_enc and not alg_comp! */
102 tcomp
= ipcomp_algorithm_lookup(sav
->alg_enc
);
104 DPRINTF(("ipcomp_init: unsupported compression algorithm %d\n",
108 sav
->alg_comp
= sav
->alg_enc
; /* set for doing histogram */
109 sav
->tdb_xform
= xsp
;
110 sav
->tdb_compalgxform
= tcomp
;
112 /* Initialize crypto session */
113 bzero(&cric
, sizeof (cric
));
114 cric
.cri_alg
= sav
->tdb_compalgxform
->type
;
116 return crypto_newsession(&sav
->tdb_cryptoid
, &cric
, crypto_support
);
120 * ipcomp_zeroize() used when IPCA is deleted
123 ipcomp_zeroize(struct secasvar
*sav
)
127 err
= crypto_freesession(sav
->tdb_cryptoid
);
128 sav
->tdb_cryptoid
= 0;
133 * ipcomp_input() gets called to uncompress an input packet
136 ipcomp_input(struct mbuf
*m
, struct secasvar
*sav
, int skip
, int protoff
)
138 struct tdb_crypto
*tc
;
139 struct cryptodesc
*crdc
;
141 struct ipcomp
*ipcomp
;
143 int hlen
= IPCOMP_HLENGTH
;
146 * Check that the next header of the IPComp is not IPComp again, before
147 * doing any real work. Given it is not possible to do double
148 * compression it means someone is playing tricks on us.
150 if (m
->m_len
< skip
+ hlen
&& (m
= m_pullup(m
, skip
+ hlen
)) == NULL
) {
151 ipcompstat
.ipcomps_hdrops
++; /*XXX*/
152 DPRINTF(("%s: m_pullup failed\n", __func__
));
155 addr
= (caddr_t
) mtod(m
, struct ip
*) + skip
;
156 ipcomp
= (struct ipcomp
*)addr
;
157 if (ipcomp
->comp_nxt
== IPPROTO_IPCOMP
) {
159 ipcompstat
.ipcomps_pdrops
++; /* XXX have our own stats? */
160 DPRINTF(("%s: recursive compression detected\n", __func__
));
164 /* Get crypto descriptors */
165 crp
= crypto_getreq(1);
168 DPRINTF(("ipcomp_input: no crypto descriptors\n"));
169 ipcompstat
.ipcomps_crypto
++;
172 /* Get IPsec-specific opaque pointer */
173 tc
= kmalloc(sizeof (*tc
), M_XDATA
,
174 M_INTWAIT
| M_ZERO
| M_NULLOK
);
178 DPRINTF(("ipcomp_input: cannot allocate tdb_crypto\n"));
179 ipcompstat
.ipcomps_crypto
++;
182 crdc
= crp
->crp_desc
;
184 crdc
->crd_skip
= skip
+ hlen
;
185 crdc
->crd_len
= m
->m_pkthdr
.len
- (skip
+ hlen
);
186 crdc
->crd_inject
= skip
;
190 /* Decompression operation */
191 crdc
->crd_alg
= sav
->tdb_compalgxform
->type
;
193 /* Crypto operation descriptor */
194 crp
->crp_ilen
= m
->m_pkthdr
.len
- (skip
+ hlen
);
195 crp
->crp_flags
= CRYPTO_F_IMBUF
;
196 crp
->crp_buf
= (caddr_t
) m
;
197 crp
->crp_callback
= ipcomp_input_cb
;
198 crp
->crp_sid
= sav
->tdb_cryptoid
;
199 crp
->crp_opaque
= (caddr_t
) tc
;
201 /* These are passed as-is to the callback */
202 tc
->tc_spi
= sav
->spi
;
203 tc
->tc_dst
= sav
->sah
->saidx
.dst
;
204 tc
->tc_proto
= sav
->sah
->saidx
.proto
;
205 tc
->tc_protoff
= protoff
;
208 return crypto_dispatch(crp
);
212 #define IPSEC_COMMON_INPUT_CB(m, sav, skip, protoff, mtag) do { \
213 if (saidx->dst.sa.sa_family == AF_INET6) { \
214 error = ipsec6_common_input_cb(m, sav, skip, protoff, mtag); \
216 error = ipsec4_common_input_cb(m, sav, skip, protoff, mtag); \
220 #define IPSEC_COMMON_INPUT_CB(m, sav, skip, protoff, mtag) \
221 (error = ipsec4_common_input_cb(m, sav, skip, protoff, mtag))
225 * IPComp input callback from the crypto driver.
228 ipcomp_input_cb(struct cryptop
*crp
)
230 struct tdb_crypto
*tc
;
233 struct secasvar
*sav
;
234 struct secasindex
*saidx
;
235 int hlen
= IPCOMP_HLENGTH
, error
, clen
;
239 tc
= (struct tdb_crypto
*) crp
->crp_opaque
;
240 KASSERT(tc
!= NULL
, ("ipcomp_input_cb: null opaque crypto data area!"));
242 protoff
= tc
->tc_protoff
;
243 m
= (struct mbuf
*) crp
->crp_buf
;
247 sav
= KEY_ALLOCSA(&tc
->tc_dst
, tc
->tc_proto
, tc
->tc_spi
);
249 ipcompstat
.ipcomps_notdb
++;
250 DPRINTF(("ipcomp_input_cb: SA expired while in crypto\n"));
251 error
= ENOBUFS
; /*XXX*/
255 saidx
= &sav
->sah
->saidx
;
256 KASSERT(saidx
->dst
.sa
.sa_family
== AF_INET
||
257 saidx
->dst
.sa
.sa_family
== AF_INET6
,
258 ("ah_input_cb: unexpected protocol family %u",
259 saidx
->dst
.sa
.sa_family
));
261 /* Check for crypto errors */
262 if (crp
->crp_etype
) {
263 /* Reset the session ID */
264 if (sav
->tdb_cryptoid
!= 0)
265 sav
->tdb_cryptoid
= crp
->crp_sid
;
267 if (crp
->crp_etype
== EAGAIN
) {
270 return crypto_dispatch(crp
);
273 ipcompstat
.ipcomps_noxform
++;
274 DPRINTF(("ipcomp_input_cb: crypto error %d\n", crp
->crp_etype
));
275 error
= crp
->crp_etype
;
278 /* Shouldn't happen... */
280 ipcompstat
.ipcomps_crypto
++;
281 DPRINTF(("ipcomp_input_cb: null mbuf returned from crypto\n"));
285 ipcompstat
.ipcomps_hist
[sav
->alg_comp
]++;
287 clen
= crp
->crp_olen
; /* Length of data after processing */
289 /* Release the crypto descriptors */
290 kfree(tc
, M_XDATA
), tc
= NULL
;
291 crypto_freereq(crp
), crp
= NULL
;
293 /* In case it's not done already, adjust the size of the mbuf chain */
294 m
->m_pkthdr
.len
= clen
+ hlen
+ skip
;
296 if (m
->m_len
< skip
+ hlen
&& (m
= m_pullup(m
, skip
+ hlen
)) == NULL
) {
297 ipcompstat
.ipcomps_hdrops
++; /*XXX*/
298 DPRINTF(("ipcomp_input_cb: m_pullup failed\n"));
299 error
= EINVAL
; /*XXX*/
303 /* Keep the next protocol field */
304 addr
= (caddr_t
) mtod(m
, struct ip
*) + skip
;
305 nproto
= ((struct ipcomp
*) addr
)->comp_nxt
;
307 /* Remove the IPCOMP header */
308 error
= m_striphdr(m
, skip
, hlen
);
310 ipcompstat
.ipcomps_hdrops
++;
311 DPRINTF(("ipcomp_input_cb: bad mbuf chain, IPCA %s/%08lx\n",
312 ipsec_address(&sav
->sah
->saidx
.dst
),
313 (u_long
) ntohl(sav
->spi
)));
317 /* Restore the Next Protocol field */
318 m_copyback(m
, protoff
, sizeof (u_int8_t
), &nproto
);
320 IPSEC_COMMON_INPUT_CB(m
, sav
, skip
, protoff
, NULL
);
339 * IPComp output routine, called by ipsec[46]_process_packet()
344 struct ipsecrequest
*isr
,
350 struct secasvar
*sav
;
351 struct comp_algo
*ipcompx
;
352 int error
, ralen
, hlen
, maxpacketsize
, roff
;
354 struct cryptodesc
*crdc
;
356 struct tdb_crypto
*tc
;
358 struct ipcomp
*ipcomp
;
361 KASSERT(sav
!= NULL
, ("ipcomp_output: null SA"));
362 ipcompx
= sav
->tdb_compalgxform
;
363 KASSERT(ipcompx
!= NULL
, ("ipcomp_output: null compression xform"));
365 ralen
= m
->m_pkthdr
.len
- skip
; /* Raw payload length before comp. */
366 hlen
= IPCOMP_HLENGTH
;
368 ipcompstat
.ipcomps_output
++;
370 /* Check for maximum packet size violations. */
371 switch (sav
->sah
->saidx
.dst
.sa
.sa_family
) {
374 maxpacketsize
= IP_MAXPACKET
;
379 maxpacketsize
= IPV6_MAXPACKET
;
383 ipcompstat
.ipcomps_nopf
++;
384 DPRINTF(("ipcomp_output: unknown/unsupported protocol family %d"
386 sav
->sah
->saidx
.dst
.sa
.sa_family
,
387 ipsec_address(&sav
->sah
->saidx
.dst
),
388 (u_long
) ntohl(sav
->spi
)));
389 error
= EPFNOSUPPORT
;
392 if (skip
+ hlen
+ ralen
> maxpacketsize
) {
393 ipcompstat
.ipcomps_toobig
++;
394 DPRINTF(("ipcomp_output: packet in IPCA %s/%08lx got too big "
395 "(len %u, max len %u)\n",
396 ipsec_address(&sav
->sah
->saidx
.dst
),
397 (u_long
) ntohl(sav
->spi
),
398 skip
+ hlen
+ ralen
, maxpacketsize
));
403 /* Update the counters */
404 ipcompstat
.ipcomps_obytes
+= m
->m_pkthdr
.len
- skip
;
408 ipcompstat
.ipcomps_hdrops
++;
409 DPRINTF(("ipcomp_output: cannot clone mbuf chain, IPCA %s/%08lx\n",
410 ipsec_address(&sav
->sah
->saidx
.dst
),
411 (u_long
) ntohl(sav
->spi
)));
416 /* Inject IPCOMP header */
417 mo
= m_makespace(m
, skip
, hlen
, &roff
);
419 ipcompstat
.ipcomps_wrap
++;
420 DPRINTF(("ipcomp_output: failed to inject IPCOMP header for "
422 ipsec_address(&sav
->sah
->saidx
.dst
),
423 (u_long
) ntohl(sav
->spi
)));
427 ipcomp
= (struct ipcomp
*)(mtod(mo
, caddr_t
) + roff
);
429 /* Initialize the IPCOMP header */
430 /* XXX alignment always correct? */
431 switch (sav
->sah
->saidx
.dst
.sa
.sa_family
) {
434 ipcomp
->comp_nxt
= mtod(m
, struct ip
*)->ip_p
;
439 ipcomp
->comp_nxt
= mtod(m
, struct ip6_hdr
*)->ip6_nxt
;
443 ipcomp
->comp_flags
= 0;
444 ipcomp
->comp_cpi
= htons((u_int16_t
) ntohl(sav
->spi
));
446 /* Fix Next Protocol in IPv4/IPv6 header */
447 prot
= IPPROTO_IPCOMP
;
448 m_copyback(m
, protoff
, sizeof(u_int8_t
), (u_char
*) &prot
);
450 /* Ok now, we can pass to the crypto processing */
452 /* Get crypto descriptors */
453 crp
= crypto_getreq(1);
455 ipcompstat
.ipcomps_crypto
++;
456 DPRINTF(("ipcomp_output: failed to acquire crypto descriptor\n"));
460 crdc
= crp
->crp_desc
;
462 /* Compression descriptor */
463 crdc
->crd_skip
= skip
+ hlen
;
464 crdc
->crd_len
= m
->m_pkthdr
.len
- (skip
+ hlen
);
465 crdc
->crd_flags
= CRD_F_COMP
;
466 crdc
->crd_inject
= skip
+ hlen
;
468 /* Compression operation */
469 crdc
->crd_alg
= ipcompx
->type
;
471 /* IPsec-specific opaque crypto info */
472 tc
= kmalloc(sizeof(struct tdb_crypto
),
473 M_XDATA
, M_INTWAIT
| M_ZERO
| M_NULLOK
);
475 ipcompstat
.ipcomps_crypto
++;
476 DPRINTF(("ipcomp_output: failed to allocate tdb_crypto\n"));
483 tc
->tc_spi
= sav
->spi
;
484 tc
->tc_dst
= sav
->sah
->saidx
.dst
;
485 tc
->tc_proto
= sav
->sah
->saidx
.proto
;
486 tc
->tc_skip
= skip
+ hlen
;
488 /* Crypto operation descriptor */
489 crp
->crp_ilen
= m
->m_pkthdr
.len
; /* Total input length */
490 crp
->crp_flags
= CRYPTO_F_IMBUF
;
491 crp
->crp_buf
= (caddr_t
) m
;
492 crp
->crp_callback
= ipcomp_output_cb
;
493 crp
->crp_opaque
= (caddr_t
) tc
;
494 crp
->crp_sid
= sav
->tdb_cryptoid
;
496 return crypto_dispatch(crp
);
504 * IPComp output callback from the crypto driver.
507 ipcomp_output_cb(struct cryptop
*crp
)
509 struct tdb_crypto
*tc
;
510 struct ipsecrequest
*isr
;
511 struct secasvar
*sav
;
513 int error
, skip
, rlen
;
515 tc
= (struct tdb_crypto
*) crp
->crp_opaque
;
516 KASSERT(tc
!= NULL
, ("ipcomp_output_cb: null opaque data area!"));
517 m
= (struct mbuf
*) crp
->crp_buf
;
519 rlen
= crp
->crp_ilen
- skip
;
524 sav
= KEY_ALLOCSA(&tc
->tc_dst
, tc
->tc_proto
, tc
->tc_spi
);
526 ipcompstat
.ipcomps_notdb
++;
527 DPRINTF(("ipcomp_output_cb: SA expired while in crypto\n"));
528 error
= ENOBUFS
; /*XXX*/
531 KASSERT(isr
->sav
== sav
, ("ipcomp_output_cb: SA changed"));
533 /* Check for crypto errors */
534 if (crp
->crp_etype
) {
535 /* Reset session ID */
536 if (sav
->tdb_cryptoid
!= 0)
537 sav
->tdb_cryptoid
= crp
->crp_sid
;
539 if (crp
->crp_etype
== EAGAIN
) {
542 return crypto_dispatch(crp
);
544 ipcompstat
.ipcomps_noxform
++;
545 DPRINTF(("ipcomp_output_cb: crypto error %d\n", crp
->crp_etype
));
546 error
= crp
->crp_etype
;
549 /* Shouldn't happen... */
551 ipcompstat
.ipcomps_crypto
++;
552 DPRINTF(("ipcomp_output_cb: bogus return buffer from crypto\n"));
556 ipcompstat
.ipcomps_hist
[sav
->alg_comp
]++;
558 if (rlen
> crp
->crp_olen
) {
559 /* Adjust the length in the IP header */
560 switch (sav
->sah
->saidx
.dst
.sa
.sa_family
) {
563 mtod(m
, struct ip
*)->ip_len
= htons(m
->m_pkthdr
.len
);
568 mtod(m
, struct ip6_hdr
*)->ip6_plen
=
569 htons(m
->m_pkthdr
.len
) - sizeof(struct ip6_hdr
);
573 ipcompstat
.ipcomps_nopf
++;
574 DPRINTF(("ipcomp_output: unknown/unsupported protocol "
575 "family %d, IPCA %s/%08lx\n",
576 sav
->sah
->saidx
.dst
.sa
.sa_family
,
577 ipsec_address(&sav
->sah
->saidx
.dst
),
578 (u_long
) ntohl(sav
->spi
)));
579 error
= EPFNOSUPPORT
;
583 /* compression was useless, we have lost time */
584 /* XXX add statistic */
587 /* Release the crypto descriptor */
591 /* NB: m is reclaimed by ipsec_process_done. */
592 error
= ipsec_process_done(m
, isr
);
607 static struct xformsw ipcomp_xformsw
= {
608 XF_IPCOMP
, XFT_COMP
, "IPcomp",
609 ipcomp_init
, ipcomp_zeroize
, ipcomp_input
,
616 xform_register(&ipcomp_xformsw
);
618 SYSINIT(ipcomp_xform_init
, SI_SUB_DRIVERS
, SI_ORDER_FIRST
, ipcomp_attach
, NULL
);