xref: /openbsd-src/sys/netinet/ipsec_output.c (revision 24bb5fcea3ed904bc467217bdaadb5dfc618d5bf)
1 /*	$OpenBSD: ipsec_output.c,v 1.83 2021/07/21 11:11:41 bluhm Exp $ */
2 /*
3  * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
4  *
5  * Copyright (c) 2000-2001 Angelos D. Keromytis.
6  *
7  * Permission to use, copy, and modify this software with or without fee
8  * is hereby granted, provided that this entire notice is included in
9  * all copies of any software which is or includes a copy or
10  * modification of this software.
11  * You may use this code under the GNU public license if you so wish. Please
12  * contribute changes back to the authors under this freer than GPL license
13  * so that we may further the use of strong encryption without limitations to
14  * all.
15  *
16  * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
18  * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
19  * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
20  * PURPOSE.
21  */
22 
23 #include "pf.h"
24 
25 #include <sys/param.h>
26 #include <sys/systm.h>
27 #include <sys/mbuf.h>
28 #include <sys/socket.h>
29 #include <sys/kernel.h>
30 #include <sys/timeout.h>
31 
32 #include <net/if.h>
33 #include <net/route.h>
34 
35 #include <netinet/in.h>
36 #include <netinet/ip.h>
37 #include <netinet/in_pcb.h>
38 #include <netinet/ip_var.h>
39 
40 #if NPF > 0
41 #include <net/pfvar.h>
42 #endif
43 
44 #include <netinet/udp.h>
45 #include <netinet/ip_ipip.h>
46 #include <netinet/ip_ah.h>
47 #include <netinet/ip_esp.h>
48 #include <netinet/ip_ipcomp.h>
49 
50 #include <crypto/cryptodev.h>
51 #include <crypto/xform.h>
52 
53 #ifdef ENCDEBUG
54 #define DPRINTF(fmt, args...)						\
55 	do {								\
56 		if (encdebug)						\
57 			printf("%s: " fmt "\n", __func__, ## args);	\
58 	} while (0)
59 #else
60 #define DPRINTF(fmt, args...)						\
61 	do { } while (0)
62 #endif
63 
64 int	udpencap_enable = 1;	/* enabled by default */
65 int	udpencap_port = 4500;	/* triggers decapsulation */
66 
67 /*
68  * Loop over a tdb chain, taking into consideration protocol tunneling. The
69  * fourth argument is set if the first encapsulation header is already in
70  * place.
71  */
72 int
73 ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready)
74 {
75 	int hlen, off, error;
76 	struct mbuf *mp;
77 #ifdef INET6
78 	struct ip6_ext ip6e;
79 	int nxt;
80 	int dstopt = 0;
81 #endif
82 
83 	int setdf = 0;
84 	struct ip *ip;
85 #ifdef INET6
86 	struct ip6_hdr *ip6;
87 #endif /* INET6 */
88 
89 #ifdef ENCDEBUG
90 	char buf[INET6_ADDRSTRLEN];
91 #endif
92 
93 	/* Check that the transform is allowed by the administrator. */
94 	if ((tdb->tdb_sproto == IPPROTO_ESP && !esp_enable) ||
95 	    (tdb->tdb_sproto == IPPROTO_AH && !ah_enable) ||
96 	    (tdb->tdb_sproto == IPPROTO_IPCOMP && !ipcomp_enable)) {
97 		DPRINTF("IPsec outbound packet dropped due to policy "
98 		    "(check your sysctls)");
99 		error = EHOSTUNREACH;
100 		goto drop;
101 	}
102 
103 	/* Sanity check. */
104 	if (!tdb->tdb_xform) {
105 		DPRINTF("uninitialized TDB");
106 		error = EHOSTUNREACH;
107 		goto drop;
108 	}
109 
110 	/* Check if the SPI is invalid. */
111 	if (tdb->tdb_flags & TDBF_INVALID) {
112 		DPRINTF("attempt to use invalid SA %s/%08x/%u",
113 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
114 		    ntohl(tdb->tdb_spi), tdb->tdb_sproto);
115 		error = ENXIO;
116 		goto drop;
117 	}
118 
119 	/* Check that the network protocol is supported */
120 	switch (tdb->tdb_dst.sa.sa_family) {
121 	case AF_INET:
122 		break;
123 
124 #ifdef INET6
125 	case AF_INET6:
126 		break;
127 #endif /* INET6 */
128 
129 	default:
130 		DPRINTF("attempt to use SA %s/%08x/%u for protocol family %d",
131 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
132 		    ntohl(tdb->tdb_spi), tdb->tdb_sproto,
133 		    tdb->tdb_dst.sa.sa_family);
134 		error = ENXIO;
135 		goto drop;
136 	}
137 
138 	/*
139 	 * Register first use if applicable, setup relevant expiration timer.
140 	 */
141 	if (tdb->tdb_first_use == 0) {
142 		tdb->tdb_first_use = gettime();
143 		if (tdb->tdb_flags & TDBF_FIRSTUSE)
144 			timeout_add_sec(&tdb->tdb_first_tmo,
145 			    tdb->tdb_exp_first_use);
146 		if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE)
147 			timeout_add_sec(&tdb->tdb_sfirst_tmo,
148 			    tdb->tdb_soft_first_use);
149 	}
150 
151 	/*
152 	 * Check for tunneling if we don't have the first header in place.
153 	 * When doing Ethernet-over-IP, we are handed an already-encapsulated
154 	 * frame, so we don't need to re-encapsulate.
155 	 */
156 	if (tunalready == 0) {
157 		/*
158 		 * If the target protocol family is different, we know we'll be
159 		 * doing tunneling.
160 		 */
161 		if (af == tdb->tdb_dst.sa.sa_family) {
162 			if (af == AF_INET)
163 				hlen = sizeof(struct ip);
164 
165 #ifdef INET6
166 			if (af == AF_INET6)
167 				hlen = sizeof(struct ip6_hdr);
168 #endif /* INET6 */
169 
170 			/* Bring the network header in the first mbuf. */
171 			if (m->m_len < hlen) {
172 				if ((m = m_pullup(m, hlen)) == NULL) {
173 					error = ENOBUFS;
174 					goto drop;
175 				}
176 			}
177 
178 			if (af == AF_INET) {
179 				ip = mtod(m, struct ip *);
180 
181 				/*
182 				 * This is not a bridge packet, remember if we
183 				 * had IP_DF.
184 				 */
185 				setdf = ip->ip_off & htons(IP_DF);
186 			}
187 
188 #ifdef INET6
189 			if (af == AF_INET6)
190 				ip6 = mtod(m, struct ip6_hdr *);
191 #endif /* INET6 */
192 		}
193 
194 		/* Do the appropriate encapsulation, if necessary. */
195 		if ((tdb->tdb_dst.sa.sa_family != af) || /* PF mismatch */
196 		    (tdb->tdb_flags & TDBF_TUNNELING) || /* Tunneling needed */
197 		    (tdb->tdb_xform->xf_type == XF_IP4) || /* ditto */
198 		    ((tdb->tdb_dst.sa.sa_family == AF_INET) &&
199 		     (tdb->tdb_dst.sin.sin_addr.s_addr != INADDR_ANY) &&
200 		     (tdb->tdb_dst.sin.sin_addr.s_addr != ip->ip_dst.s_addr)) ||
201 #ifdef INET6
202 		    ((tdb->tdb_dst.sa.sa_family == AF_INET6) &&
203 		     (!IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_dst.sin6.sin6_addr)) &&
204 		     (!IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr,
205 		      &ip6->ip6_dst))) ||
206 #endif /* INET6 */
207 		    0) {
208 			/* Fix IPv4 header checksum and length. */
209 			if (af == AF_INET) {
210 				if (m->m_len < sizeof(struct ip))
211 					if ((m = m_pullup(m,
212 					    sizeof(struct ip))) == NULL) {
213 						error = ENOBUFS;
214 						goto drop;
215 					}
216 
217 				ip = mtod(m, struct ip *);
218 				ip->ip_len = htons(m->m_pkthdr.len);
219 				ip->ip_sum = 0;
220 				ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
221 			}
222 
223 #ifdef INET6
224 			/* Fix IPv6 header payload length. */
225 			if (af == AF_INET6) {
226 				if (m->m_len < sizeof(struct ip6_hdr))
227 					if ((m = m_pullup(m,
228 					    sizeof(struct ip6_hdr))) == NULL) {
229 						error = ENOBUFS;
230 						goto drop;
231 					}
232 
233 				if (m->m_pkthdr.len - sizeof(*ip6) >
234 				    IPV6_MAXPACKET) {
235 					/* No jumbogram support. */
236 					error = ENXIO;	/*?*/
237 					goto drop;
238 				}
239 				ip6 = mtod(m, struct ip6_hdr *);
240 				ip6->ip6_plen = htons(m->m_pkthdr.len
241 				    - sizeof(*ip6));
242 			}
243 #endif /* INET6 */
244 
245 			/* Encapsulate -- the last two arguments are unused. */
246 			error = ipip_output(m, tdb, &mp, 0, 0);
247 			if ((mp == NULL) && (!error))
248 				error = EFAULT;
249 			m = mp;
250 			mp = NULL;
251 			if (error)
252 				goto drop;
253 
254 			if (tdb->tdb_dst.sa.sa_family == AF_INET && setdf) {
255 				if (m->m_len < sizeof(struct ip))
256 					if ((m = m_pullup(m,
257 					    sizeof(struct ip))) == NULL) {
258 						error = ENOBUFS;
259 						goto drop;
260 					}
261 
262 				ip = mtod(m, struct ip *);
263 				ip->ip_off |= htons(IP_DF);
264 			}
265 
266 			/* Remember that we appended a tunnel header. */
267 			tdb->tdb_flags |= TDBF_USEDTUNNEL;
268 		}
269 
270 		/* We may be done with this TDB */
271 		if (tdb->tdb_xform->xf_type == XF_IP4)
272 			return ipsp_process_done(m, tdb);
273 	} else {
274 		/*
275 		 * If this is just an IP-IP TDB and we're told there's
276 		 * already an encapsulation header, move on.
277 		 */
278 		if (tdb->tdb_xform->xf_type == XF_IP4)
279 			return ipsp_process_done(m, tdb);
280 	}
281 
282 	/* Extract some information off the headers. */
283 	switch (tdb->tdb_dst.sa.sa_family) {
284 	case AF_INET:
285 		ip = mtod(m, struct ip *);
286 		hlen = ip->ip_hl << 2;
287 		off = offsetof(struct ip, ip_p);
288 		break;
289 
290 #ifdef INET6
291 	case AF_INET6:
292 		ip6 = mtod(m, struct ip6_hdr *);
293 		hlen = sizeof(struct ip6_hdr);
294 		off = offsetof(struct ip6_hdr, ip6_nxt);
295 		nxt = ip6->ip6_nxt;
296 		/*
297 		 * chase mbuf chain to find the appropriate place to
298 		 * put AH/ESP/IPcomp header.
299 		 *	IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]
300 		 */
301 		do {
302 			switch (nxt) {
303 			case IPPROTO_AH:
304 			case IPPROTO_ESP:
305 			case IPPROTO_IPCOMP:
306 				/*
307 				 * we should not skip security header added
308 				 * beforehand.
309 				 */
310 				goto exitip6loop;
311 
312 			case IPPROTO_HOPOPTS:
313 			case IPPROTO_DSTOPTS:
314 			case IPPROTO_ROUTING:
315 				/*
316 				 * if we see 2nd destination option header,
317 				 * we should stop there.
318 				 */
319 				if (nxt == IPPROTO_DSTOPTS && dstopt)
320 					goto exitip6loop;
321 
322 				if (nxt == IPPROTO_DSTOPTS) {
323 					/*
324 					 * seen 1st or 2nd destination option.
325 					 * next time we see one, it must be 2nd.
326 					 */
327 					dstopt = 1;
328 				} else if (nxt == IPPROTO_ROUTING) {
329 					/*
330 					 * if we see destination option next
331 					 * time, it must be dest2.
332 					 */
333 					dstopt = 2;
334 				}
335 				if (m->m_pkthdr.len < hlen + sizeof(ip6e)) {
336 					error = EINVAL;
337 					goto drop;
338 				}
339 				/* skip this header */
340 				m_copydata(m, hlen, sizeof(ip6e),
341 				    (caddr_t)&ip6e);
342 				nxt = ip6e.ip6e_nxt;
343 				off = hlen + offsetof(struct ip6_ext, ip6e_nxt);
344 				/*
345 				 * we will never see nxt == IPPROTO_AH
346 				 * so it is safe to omit AH case.
347 				 */
348 				hlen += (ip6e.ip6e_len + 1) << 3;
349 				break;
350 			default:
351 				goto exitip6loop;
352 			}
353 		} while (hlen < m->m_pkthdr.len);
354 	exitip6loop:
355 		break;
356 #endif /* INET6 */
357 	default:
358 		error = EINVAL;
359 		goto drop;
360 	}
361 
362 	if (m->m_pkthdr.len < hlen) {
363 		error = EINVAL;
364 		goto drop;
365 	}
366 
367 	ipsecstat_add(ipsec_ouncompbytes, m->m_pkthdr.len);
368 	tdb->tdb_ouncompbytes += m->m_pkthdr.len;
369 
370 	/* Non expansion policy for IPCOMP */
371 	if (tdb->tdb_sproto == IPPROTO_IPCOMP) {
372 		if ((m->m_pkthdr.len - hlen) < tdb->tdb_compalgxform->minlen) {
373 			/* No need to compress, leave the packet untouched */
374 			ipcompstat_inc(ipcomps_minlen);
375 			return ipsp_process_done(m, tdb);
376 		}
377 	}
378 
379 	/* Invoke the IPsec transform. */
380 	return (*(tdb->tdb_xform->xf_output))(m, tdb, NULL, hlen, off);
381 
382  drop:
383 	m_freem(m);
384 	return error;
385 }
386 
387 /*
388  * IPsec output callback, called directly by the crypto driver.
389  */
390 void
391 ipsec_output_cb(struct cryptop *crp)
392 {
393 	struct tdb_crypto *tc = (struct tdb_crypto *) crp->crp_opaque;
394 	struct mbuf *m = (struct mbuf *) crp->crp_buf;
395 	struct tdb *tdb = NULL;
396 	int error, ilen, olen;
397 
398 	KERNEL_ASSERT_LOCKED();
399 
400 	if (m == NULL) {
401 		DPRINTF("bogus returned buffer from crypto");
402 		ipsecstat_inc(ipsec_crypto);
403 		goto droponly;
404 	}
405 
406 	NET_LOCK();
407 	tdb = gettdb(tc->tc_rdomain, tc->tc_spi, &tc->tc_dst, tc->tc_proto);
408 	if (tdb == NULL) {
409 		DPRINTF("TDB is expired while in crypto");
410 		ipsecstat_inc(ipsec_notdb);
411 		goto baddone;
412 	}
413 
414 	/* Check for crypto errors. */
415 	if (crp->crp_etype) {
416 		if (crp->crp_etype == EAGAIN) {
417 			/* Reset the session ID */
418 			if (tdb->tdb_cryptoid != 0)
419 				tdb->tdb_cryptoid = crp->crp_sid;
420 			NET_UNLOCK();
421 			error = crypto_dispatch(crp);
422 			if (error) {
423 				DPRINTF("crypto dispatch error %d", error);
424 				ipsecstat_inc(ipsec_odrops);
425 				tdb->tdb_odrops++;
426 			}
427 			return;
428 		}
429 		DPRINTF("crypto error %d", crp->crp_etype);
430 		ipsecstat_inc(ipsec_noxform);
431 		goto baddone;
432 	}
433 
434 	olen = crp->crp_olen;
435 	ilen = crp->crp_ilen;
436 
437 	/* Release crypto descriptors. */
438 	crypto_freereq(crp);
439 
440 	switch (tdb->tdb_sproto) {
441 	case IPPROTO_ESP:
442 		error = esp_output_cb(tdb, tc, m, ilen, olen);
443 		break;
444 	case IPPROTO_AH:
445 		error = ah_output_cb(tdb, tc, m, ilen, olen);
446 		break;
447 	case IPPROTO_IPCOMP:
448 		error = ipcomp_output_cb(tdb, tc, m, ilen, olen);
449 		break;
450 	default:
451 		panic("%s: unknown/unsupported security protocol %d",
452 		    __func__, tdb->tdb_sproto);
453 	}
454 
455 	NET_UNLOCK();
456 	if (error) {
457 		ipsecstat_inc(ipsec_odrops);
458 		tdb->tdb_odrops++;
459 	}
460 	return;
461 
462  baddone:
463 	NET_UNLOCK();
464  droponly:
465 	if (tdb != NULL)
466 		tdb->tdb_odrops++;
467 	m_freem(m);
468 	free(tc, M_XDATA, 0);
469 	crypto_freereq(crp);
470 	ipsecstat_inc(ipsec_odrops);
471 }
472 
473 /*
474  * Called by the IPsec output transform callbacks, to transmit the packet
475  * or do further processing, as necessary.
476  */
477 int
478 ipsp_process_done(struct mbuf *m, struct tdb *tdb)
479 {
480 	struct ip *ip;
481 #ifdef INET6
482 	struct ip6_hdr *ip6;
483 #endif /* INET6 */
484 	struct tdb_ident *tdbi;
485 	struct m_tag *mtag;
486 	int roff, error;
487 
488 	tdb->tdb_last_used = gettime();
489 
490 	if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0) {
491 		struct mbuf *mi;
492 		struct udphdr *uh;
493 		int iphlen;
494 
495 		if (!udpencap_enable || !udpencap_port) {
496 			error = ENXIO;
497 			goto drop;
498 		}
499 
500 		switch (tdb->tdb_dst.sa.sa_family) {
501 		case AF_INET:
502 			iphlen = sizeof(struct ip);
503 			break;
504 #ifdef INET6
505 		case AF_INET6:
506 			iphlen = sizeof(struct ip6_hdr);
507 			break;
508 #endif /* INET6 */
509 		default:
510 			DPRINTF("unknown protocol family (%d)",
511 			    tdb->tdb_dst.sa.sa_family);
512 			error = ENXIO;
513 			goto drop;
514 		}
515 
516 		mi = m_makespace(m, iphlen, sizeof(struct udphdr), &roff);
517 		if (mi == NULL) {
518 			error = ENOMEM;
519 			goto drop;
520 		}
521 		uh = (struct udphdr *)(mtod(mi, caddr_t) + roff);
522 		uh->uh_sport = uh->uh_dport = htons(udpencap_port);
523 		if (tdb->tdb_udpencap_port)
524 			uh->uh_dport = tdb->tdb_udpencap_port;
525 
526 		uh->uh_ulen = htons(m->m_pkthdr.len - iphlen);
527 		uh->uh_sum = 0;
528 #ifdef INET6
529 		if (tdb->tdb_dst.sa.sa_family == AF_INET6)
530 			m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
531 #endif /* INET6 */
532 		espstat_inc(esps_udpencout);
533 	}
534 
535 	switch (tdb->tdb_dst.sa.sa_family) {
536 	case AF_INET:
537 		/* Fix the header length, for AH processing. */
538 		ip = mtod(m, struct ip *);
539 		ip->ip_len = htons(m->m_pkthdr.len);
540 		if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0)
541 			ip->ip_p = IPPROTO_UDP;
542 		break;
543 
544 #ifdef INET6
545 	case AF_INET6:
546 		/* Fix the header length, for AH processing. */
547 		if (m->m_pkthdr.len < sizeof(*ip6)) {
548 			error = ENXIO;
549 			goto drop;
550 		}
551 		if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) {
552 			/* No jumbogram support. */
553 			error = ENXIO;
554 			goto drop;
555 		}
556 		ip6 = mtod(m, struct ip6_hdr *);
557 		ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6));
558 		if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0)
559 			ip6->ip6_nxt = IPPROTO_UDP;
560 		break;
561 #endif /* INET6 */
562 
563 	default:
564 		DPRINTF("unknown protocol family (%d)",
565 		    tdb->tdb_dst.sa.sa_family);
566 		error = ENXIO;
567 		goto drop;
568 	}
569 
570 	/*
571 	 * Add a record of what we've done or what needs to be done to the
572 	 * packet.
573 	 */
574 	mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, sizeof(struct tdb_ident),
575 	    M_NOWAIT);
576 	if (mtag == NULL) {
577 		DPRINTF("could not allocate packet tag");
578 		error = ENOMEM;
579 		goto drop;
580 	}
581 
582 	tdbi = (struct tdb_ident *)(mtag + 1);
583 	tdbi->dst = tdb->tdb_dst;
584 	tdbi->proto = tdb->tdb_sproto;
585 	tdbi->spi = tdb->tdb_spi;
586 	tdbi->rdomain = tdb->tdb_rdomain;
587 
588 	m_tag_prepend(m, mtag);
589 
590 	ipsecstat_inc(ipsec_opackets);
591 	ipsecstat_add(ipsec_obytes, m->m_pkthdr.len);
592 	tdb->tdb_opackets++;
593 	tdb->tdb_obytes += m->m_pkthdr.len;
594 
595 	/* If there's another (bundled) TDB to apply, do so. */
596 	if (tdb->tdb_onext)
597 		return ipsp_process_packet(m, tdb->tdb_onext,
598 		    tdb->tdb_dst.sa.sa_family, 0);
599 
600 #if NPF > 0
601 	/* Add pf tag if requested. */
602 	pf_tag_packet(m, tdb->tdb_tag, -1);
603 	pf_pkt_addr_changed(m);
604 #endif
605 	if (tdb->tdb_rdomain != tdb->tdb_rdomain_post)
606 		m->m_pkthdr.ph_rtableid = tdb->tdb_rdomain_post;
607 
608 	/*
609 	 * We're done with IPsec processing, transmit the packet using the
610 	 * appropriate network protocol (IP or IPv6). SPD lookup will be
611 	 * performed again there.
612 	 */
613 	switch (tdb->tdb_dst.sa.sa_family) {
614 	case AF_INET:
615 		return (ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL, 0));
616 
617 #ifdef INET6
618 	case AF_INET6:
619 		/*
620 		 * We don't need massage, IPv6 header fields are always in
621 		 * net endian.
622 		 */
623 		return (ip6_output(m, NULL, NULL, 0, NULL, NULL));
624 #endif /* INET6 */
625 	}
626 	error = EINVAL; /* Not reached. */
627 
628  drop:
629 	m_freem(m);
630 	return error;
631 }
632 
633 ssize_t
634 ipsec_hdrsz(struct tdb *tdbp)
635 {
636 	ssize_t adjust;
637 
638 	switch (tdbp->tdb_sproto) {
639 	case IPPROTO_IPIP:
640 		adjust = 0;
641 		break;
642 
643 	case IPPROTO_ESP:
644 		if (tdbp->tdb_encalgxform == NULL)
645 			return (-1);
646 
647 		/* Header length */
648 		adjust = 2 * sizeof(u_int32_t) + tdbp->tdb_ivlen;
649 		if (tdbp->tdb_flags & TDBF_UDPENCAP)
650 			adjust += sizeof(struct udphdr);
651 		/* Authenticator */
652 		if (tdbp->tdb_authalgxform != NULL)
653 			adjust += tdbp->tdb_authalgxform->authsize;
654 		/* Padding */
655 		adjust += MAX(4, tdbp->tdb_encalgxform->blocksize);
656 		break;
657 
658 	case IPPROTO_AH:
659 		if (tdbp->tdb_authalgxform == NULL)
660 			return (-1);
661 
662 		adjust = AH_FLENGTH + sizeof(u_int32_t);
663 		adjust += tdbp->tdb_authalgxform->authsize;
664 		break;
665 
666 	default:
667 		return (-1);
668 	}
669 
670 	if (!(tdbp->tdb_flags & TDBF_TUNNELING) &&
671 	    !(tdbp->tdb_flags & TDBF_USEDTUNNEL))
672 		return (adjust);
673 
674 	switch (tdbp->tdb_dst.sa.sa_family) {
675 	case AF_INET:
676 		adjust += sizeof(struct ip);
677 		break;
678 #ifdef INET6
679 	case AF_INET6:
680 		adjust += sizeof(struct ip6_hdr);
681 		break;
682 #endif /* INET6 */
683 	}
684 
685 	return (adjust);
686 }
687 
688 void
689 ipsec_adjust_mtu(struct mbuf *m, u_int32_t mtu)
690 {
691 	struct tdb_ident *tdbi;
692 	struct tdb *tdbp;
693 	struct m_tag *mtag;
694 	ssize_t adjust;
695 
696 	NET_ASSERT_LOCKED();
697 
698 	for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL); mtag;
699 	     mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, mtag)) {
700 		tdbi = (struct tdb_ident *)(mtag + 1);
701 		tdbp = gettdb(tdbi->rdomain, tdbi->spi, &tdbi->dst,
702 		    tdbi->proto);
703 		if (tdbp == NULL)
704 			break;
705 
706 		if ((adjust = ipsec_hdrsz(tdbp)) == -1)
707 			break;
708 
709 		mtu -= adjust;
710 		tdbp->tdb_mtu = mtu;
711 		tdbp->tdb_mtutimeout = gettime() + ip_mtudisc_timeout;
712 		DPRINTF("spi %08x mtu %d adjust %ld mbuf %p",
713 		    ntohl(tdbp->tdb_spi), tdbp->tdb_mtu, adjust, m);
714 	}
715 }
716