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