xref: /openbsd-src/sys/netinet/ip_ipcomp.c (revision 824adb5411e4389b29bae28eba5c2c2bbd147f34)
1 /* $OpenBSD: ip_ipcomp.c,v 1.74 2021/07/27 17:13:03 mvs 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 	case SADB_X_CALG_LZS:
94 		tcomp = &comp_algo_lzs;
95 		break;
96 
97 	default:
98 		DPRINTF("unsupported compression algorithm %d specified",
99 		    ii->ii_compalg);
100 		return EINVAL;
101 	}
102 
103 	tdbp->tdb_compalgxform = tcomp;
104 
105 	DPRINTF("initialized TDB with ipcomp algorithm %s", tcomp->name);
106 
107 	tdbp->tdb_xform = xsp;
108 
109 	/* Initialize crypto session */
110 	memset(&cric, 0, sizeof(cric));
111 	cric.cri_alg = tdbp->tdb_compalgxform->type;
112 
113 	KERNEL_LOCK();
114 	error = crypto_newsession(&tdbp->tdb_cryptoid, &cric, 0);
115 	KERNEL_UNLOCK();
116 	return error;
117 }
118 
119 /*
120  * ipcomp_zeroize() used when IPCA is deleted
121  */
122 int
123 ipcomp_zeroize(struct tdb *tdbp)
124 {
125 	int error;
126 
127 	KERNEL_LOCK();
128 	error = crypto_freesession(tdbp->tdb_cryptoid);
129 	KERNEL_UNLOCK();
130 	tdbp->tdb_cryptoid = 0;
131 	return error;
132 }
133 
134 /*
135  * ipcomp_input() gets called to uncompress an input packet
136  */
137 int
138 ipcomp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
139 {
140 	const struct comp_algo *ipcompx = tdb->tdb_compalgxform;
141 	struct tdb_crypto *tc;
142 	int hlen, error;
143 
144 	struct cryptodesc *crdc = NULL;
145 	struct cryptop *crp;
146 
147 	hlen = IPCOMP_HLENGTH;
148 
149 	/* Get crypto descriptors */
150 	crp = crypto_getreq(1);
151 	if (crp == NULL) {
152 		m_freem(m);
153 		DPRINTF("failed to acquire crypto descriptors");
154 		ipcompstat_inc(ipcomps_crypto);
155 		return ENOBUFS;
156 	}
157 	/* Get IPsec-specific opaque pointer */
158 	tc = malloc(sizeof(*tc), M_XDATA, M_NOWAIT | M_ZERO);
159 	if (tc == NULL) {
160 		m_freem(m);
161 		crypto_freereq(crp);
162 		DPRINTF("failed to allocate tdb_crypto");
163 		ipcompstat_inc(ipcomps_crypto);
164 		return ENOBUFS;
165 	}
166 	crdc = &crp->crp_desc[0];
167 
168 	crdc->crd_skip = skip + hlen;
169 	crdc->crd_len = m->m_pkthdr.len - (skip + hlen);
170 	crdc->crd_inject = skip;
171 
172 	/* Decompression operation */
173 	crdc->crd_alg = ipcompx->type;
174 
175 	/* Crypto operation descriptor */
176 	crp->crp_ilen = m->m_pkthdr.len - (skip + hlen);
177 	crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_MPSAFE | CRYPTO_F_NOQUEUE;
178 	crp->crp_buf = (caddr_t)m;
179 	crp->crp_callback = ipsec_input_cb;
180 	crp->crp_sid = tdb->tdb_cryptoid;
181 	crp->crp_opaque = (caddr_t)tc;
182 
183 	/* These are passed as-is to the callback */
184 	tc->tc_skip = skip;
185 	tc->tc_protoff = protoff;
186 	tc->tc_spi = tdb->tdb_spi;
187 	tc->tc_proto = IPPROTO_IPCOMP;
188 	tc->tc_rdomain = tdb->tdb_rdomain;
189 	tc->tc_dst = tdb->tdb_dst;
190 
191 	error = crypto_dispatch(crp);
192 	return error;
193 }
194 
195 int
196 ipcomp_input_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m, int clen)
197 {
198 	int skip, protoff, roff, hlen = IPCOMP_HLENGTH;
199 	u_int8_t nproto;
200 	u_int64_t ibytes;
201 	struct mbuf *m1, *mo;
202 	struct ipcomp  *ipcomp;
203 	caddr_t addr;
204 #ifdef ENCDEBUG
205 	char buf[INET6_ADDRSTRLEN];
206 #endif
207 
208 	NET_ASSERT_LOCKED();
209 
210 	skip = tc->tc_skip;
211 	protoff = tc->tc_protoff;
212 
213 	/* update the counters */
214 	ibytes = m->m_pkthdr.len - (skip + hlen);
215 	tdb->tdb_cur_bytes += ibytes;
216 	tdb->tdb_ibytes += ibytes;
217 	ipcompstat_add(ipcomps_ibytes, ibytes);
218 
219 	/* Hard expiration */
220 	if ((tdb->tdb_flags & TDBF_BYTES) &&
221 	    (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) {
222 		pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
223 		tdb_delete(tdb);
224 		goto baddone;
225 	}
226 	/* Notify on soft expiration */
227 	if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
228 	    (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) {
229 		pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
230 		tdb->tdb_flags &= ~TDBF_SOFT_BYTES;	/* Turn off checking */
231 	}
232 
233 	/* In case it's not done already, adjust the size of the mbuf chain */
234 	m->m_pkthdr.len = clen + hlen + skip;
235 
236 	if ((m->m_len < skip + hlen) && (m = m_pullup(m, skip + hlen)) == 0) {
237 		ipcompstat_inc(ipcomps_hdrops);
238 		goto baddone;
239 	}
240 
241 	/* Find the beginning of the IPCOMP header */
242 	m1 = m_getptr(m, skip, &roff);
243 	if (m1 == NULL) {
244 		DPRINTF("bad mbuf chain, IPCA %s/%08x",
245 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
246 		    ntohl(tdb->tdb_spi));
247 		ipcompstat_inc(ipcomps_hdrops);
248 		goto baddone;
249 	}
250 	/* Keep the next protocol field */
251 	addr = (caddr_t) mtod(m, struct ip *) + skip;
252 	ipcomp = (struct ipcomp *) addr;
253 	nproto = ipcomp->ipcomp_nh;
254 
255 	/* Remove the IPCOMP header from the mbuf */
256 	if (roff == 0) {
257 		/* The IPCOMP header is at the beginning of m1 */
258 		m_adj(m1, hlen);
259 		/*
260 		 * If m1 is the first mbuf, it has set M_PKTHDR and m_adj()
261 		 * has already adjusted the packet header length for us.
262 		 */
263 		if (m1 != m)
264 			m->m_pkthdr.len -= hlen;
265 	} else if (roff + hlen >= m1->m_len) {
266 		int adjlen;
267 
268 		if (roff + hlen > m1->m_len) {
269 			adjlen = roff + hlen - m1->m_len;
270 
271 			/* Adjust the next mbuf by the remainder */
272 			m_adj(m1->m_next, adjlen);
273 
274 			/*
275 			 * The second mbuf is guaranteed not to have a
276 			 * pkthdr...
277 			 */
278 			m->m_pkthdr.len -= adjlen;
279 		}
280 		/* Now, let's unlink the mbuf chain for a second... */
281 		mo = m1->m_next;
282 		m1->m_next = NULL;
283 
284 		/* ...and trim the end of the first part of the chain...sick */
285 		adjlen = m1->m_len - roff;
286 		m_adj(m1, -adjlen);
287 		/*
288 		 * If m1 is the first mbuf, it has set M_PKTHDR and m_adj()
289 		 * has already adjusted the packet header length for us.
290 		 */
291 		if (m1 != m)
292 			m->m_pkthdr.len -= adjlen;
293 
294 		/* Finally, let's relink */
295 		m1->m_next = mo;
296 	} else {
297 		memmove(mtod(m1, u_char *) + roff,
298 		    mtod(m1, u_char *) + roff + hlen,
299 		    m1->m_len - (roff + hlen));
300 		m1->m_len -= hlen;
301 		m->m_pkthdr.len -= hlen;
302 	}
303 
304 	/* Release the crypto descriptors */
305 	free(tc, M_XDATA, 0);
306 
307 	/* Restore the Next Protocol field */
308 	m_copyback(m, protoff, sizeof(u_int8_t), &nproto, M_NOWAIT);
309 
310 	/* Back to generic IPsec input processing */
311 	return ipsec_common_input_cb(m, tdb, skip, protoff);
312 
313  baddone:
314 	m_freem(m);
315 	free(tc, M_XDATA, 0);
316 	return -1;
317 }
318 
319 /*
320  * IPComp output routine, called by ipsp_process_packet()
321  */
322 int
323 ipcomp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
324     int protoff)
325 {
326 	const struct comp_algo *ipcompx = tdb->tdb_compalgxform;
327 	int error, hlen;
328 	struct cryptodesc *crdc = NULL;
329 	struct cryptop *crp = NULL;
330 	struct tdb_crypto *tc;
331 	struct mbuf    *mi;
332 #ifdef ENCDEBUG
333 	char buf[INET6_ADDRSTRLEN];
334 #endif
335 #if NBPFILTER > 0
336 	struct ifnet *encif;
337 
338 	if ((encif = enc_getif(0, tdb->tdb_tap)) != NULL) {
339 		encif->if_opackets++;
340 		encif->if_obytes += m->m_pkthdr.len;
341 
342 		if (encif->if_bpf) {
343 			struct enchdr hdr;
344 
345 			memset(&hdr, 0, sizeof(hdr));
346 
347 			hdr.af = tdb->tdb_dst.sa.sa_family;
348 			hdr.spi = tdb->tdb_spi;
349 
350 			bpf_mtap_hdr(encif->if_bpf, (char *)&hdr,
351 			    ENC_HDRLEN, m, BPF_DIRECTION_OUT);
352 		}
353 	}
354 #endif
355 	hlen = IPCOMP_HLENGTH;
356 
357 	ipcompstat_inc(ipcomps_output);
358 
359 	switch (tdb->tdb_dst.sa.sa_family) {
360 	case AF_INET:
361 		/* Check for IPv4 maximum packet size violations */
362 		/*
363 		 * Since compression is going to reduce the size, no need to
364 		 * worry
365 		 */
366 		if (m->m_pkthdr.len + hlen > IP_MAXPACKET) {
367 			DPRINTF("packet in IPCA %s/%08x got too big",
368 			    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
369 			    ntohl(tdb->tdb_spi));
370 			ipcompstat_inc(ipcomps_toobig);
371 			error = EMSGSIZE;
372 			goto drop;
373 		}
374 		break;
375 
376 #ifdef INET6
377 	case AF_INET6:
378 		/* Check for IPv6 maximum packet size violations */
379 		if (m->m_pkthdr.len + hlen > IPV6_MAXPACKET) {
380 			DPRINTF("packet in IPCA %s/%08x got too big",
381 			    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
382 			    ntohl(tdb->tdb_spi));
383 			ipcompstat_inc(ipcomps_toobig);
384 			error = EMSGSIZE;
385 			goto drop;
386 		}
387 		break;
388 #endif /* INET6 */
389 
390 	default:
391 		DPRINTF("unknown/unsupported protocol family %d, IPCA %s/%08x",
392 		    tdb->tdb_dst.sa.sa_family,
393 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
394 		    ntohl(tdb->tdb_spi));
395 		ipcompstat_inc(ipcomps_nopf);
396 		error = EPFNOSUPPORT;
397 		goto drop;
398 	}
399 
400 	/* Update the counters */
401 
402 	tdb->tdb_cur_bytes += m->m_pkthdr.len - skip;
403 	ipcompstat_add(ipcomps_obytes, m->m_pkthdr.len - skip);
404 
405 	/* Hard byte expiration */
406 	if ((tdb->tdb_flags & TDBF_BYTES) &&
407 	    (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) {
408 		pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
409 		tdb_delete(tdb);
410 		error = EINVAL;
411 		goto drop;
412 	}
413 	/* Soft byte expiration */
414 	if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
415 	    (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) {
416 		pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
417 		tdb->tdb_flags &= ~TDBF_SOFT_BYTES;	/* Turn off checking */
418 	}
419 	/*
420 	 * Loop through mbuf chain; if we find a readonly mbuf,
421 	 * copy the packet.
422 	 */
423 	mi = m;
424 	while (mi != NULL && !M_READONLY(mi))
425 		mi = mi->m_next;
426 
427 	if (mi != NULL) {
428 		struct mbuf *n = m_dup_pkt(m, 0, M_DONTWAIT);
429 
430 		if (n == NULL) {
431 			DPRINTF("bad mbuf chain, IPCA %s/%08x",
432 			    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
433 			    ntohl(tdb->tdb_spi));
434 			ipcompstat_inc(ipcomps_hdrops);
435 			error = ENOBUFS;
436 			goto drop;
437 		}
438 
439 		m_freem(m);
440 		m = n;
441 	}
442 	/* Ok now, we can pass to the crypto processing */
443 
444 	/* Get crypto descriptors */
445 	crp = crypto_getreq(1);
446 	if (crp == NULL) {
447 		DPRINTF("failed to acquire crypto descriptors");
448 		ipcompstat_inc(ipcomps_crypto);
449 		error = ENOBUFS;
450 		goto drop;
451 	}
452 	crdc = &crp->crp_desc[0];
453 
454 	/* Compression descriptor */
455 	crdc->crd_skip = skip;
456 	crdc->crd_len = m->m_pkthdr.len - skip;
457 	crdc->crd_flags = CRD_F_COMP;
458 	crdc->crd_inject = skip;
459 
460 	/* Compression operation */
461 	crdc->crd_alg = ipcompx->type;
462 
463 	/* IPsec-specific opaque crypto info */
464 	tc = malloc(sizeof(*tc), M_XDATA, M_NOWAIT | M_ZERO);
465 	if (tc == NULL) {
466 		DPRINTF("failed to allocate tdb_crypto");
467 		ipcompstat_inc(ipcomps_crypto);
468 		error = ENOBUFS;
469 		goto drop;
470 	}
471 
472 	tc->tc_spi = tdb->tdb_spi;
473 	tc->tc_proto = tdb->tdb_sproto;
474 	tc->tc_skip = skip;
475 	tc->tc_rdomain = tdb->tdb_rdomain;
476 	tc->tc_dst = tdb->tdb_dst;
477 
478 	/* Crypto operation descriptor */
479 	crp->crp_ilen = m->m_pkthdr.len;	/* Total input length */
480 	crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_MPSAFE | CRYPTO_F_NOQUEUE;
481 	crp->crp_buf = (caddr_t)m;
482 	crp->crp_callback = ipsec_output_cb;
483 	crp->crp_opaque = (caddr_t)tc;
484 	crp->crp_sid = tdb->tdb_cryptoid;
485 
486 	error = crypto_dispatch(crp);
487 	return error;
488 
489  drop:
490 	m_freem(m);
491 	crypto_freereq(crp);
492 	return error;
493 }
494 
495 /*
496  * IPComp output callback.
497  */
498 int
499 ipcomp_output_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m,
500     int ilen, int olen)
501 {
502 	struct mbuf *mo;
503 	int skip, rlen, roff;
504 	u_int16_t cpi;
505 	struct ip *ip;
506 #ifdef INET6
507 	struct ip6_hdr *ip6;
508 #endif
509 	struct ipcomp  *ipcomp;
510 #ifdef ENCDEBUG
511 	char buf[INET6_ADDRSTRLEN];
512 #endif
513 
514 	skip = tc->tc_skip;
515 	rlen = ilen - skip;
516 
517 	/* Check sizes. */
518 	if (rlen <= olen + IPCOMP_HLENGTH) {
519 		/* Compression was useless, we have lost time. */
520 		ipcompstat_inc(ipcomps_minlen); /* misnomer, but like to count */
521 		goto skiphdr;
522 	}
523 
524 	/* Inject IPCOMP header */
525 	mo = m_makespace(m, skip, IPCOMP_HLENGTH, &roff);
526 	if (mo == NULL) {
527 		DPRINTF("ailed to inject IPCOMP header for IPCA %s/%08x",
528 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
529 		    ntohl(tdb->tdb_spi));
530 		ipcompstat_inc(ipcomps_wrap);
531 		goto baddone;
532 	}
533 
534 	/* Initialize the IPCOMP header */
535 	ipcomp = (struct ipcomp *)(mtod(mo, caddr_t) + roff);
536 	memset(ipcomp, 0, sizeof(struct ipcomp));
537 	cpi = (u_int16_t) ntohl(tdb->tdb_spi);
538 	ipcomp->ipcomp_cpi = htons(cpi);
539 
540 	/* m_pullup before ? */
541 	switch (tdb->tdb_dst.sa.sa_family) {
542 	case AF_INET:
543 		ip = mtod(m, struct ip *);
544 		ipcomp->ipcomp_nh = ip->ip_p;
545 		ip->ip_p = IPPROTO_IPCOMP;
546 		break;
547 #ifdef INET6
548 	case AF_INET6:
549 		ip6 = mtod(m, struct ip6_hdr *);
550 		ipcomp->ipcomp_nh = ip6->ip6_nxt;
551 		ip6->ip6_nxt = IPPROTO_IPCOMP;
552 		break;
553 #endif
554 	default:
555 		DPRINTF("unsupported protocol family %d, IPCA %s/%08x",
556 		    tdb->tdb_dst.sa.sa_family,
557 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
558 		    ntohl(tdb->tdb_spi));
559 		ipcompstat_inc(ipcomps_nopf);
560 		goto baddone;
561 	}
562 
563  skiphdr:
564 	/* Release the crypto descriptor. */
565 	free(tc, M_XDATA, 0);
566 
567 	if (ipsp_process_done(m, tdb)) {
568 		ipcompstat_inc(ipcomps_outfail);
569 		return -1;
570 	}
571 	return 0;
572 
573  baddone:
574 	m_freem(m);
575 	free(tc, M_XDATA, 0);
576 	return -1;
577 }
578