xref: /openbsd/sys/netinet/ip_ipcomp.c (revision fb8de0f1)
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
ipcomp_attach(void)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
ipcomp_init(struct tdb * tdbp,const struct xformsw * xsp,struct ipsecinit * ii)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
ipcomp_zeroize(struct tdb * tdbp)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
ipcomp_input(struct mbuf ** mp,struct tdb * tdb,int skip,int protoff)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
ipcomp_output(struct mbuf * m,struct tdb * tdb,int skip,int protoff)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 }
539