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