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