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