xref: /openbsd-src/sys/netinet/ipsec_output.c (revision 824adb5411e4389b29bae28eba5c2c2bbd147f34)
1 /*	$OpenBSD: ipsec_output.c,v 1.87 2021/10/05 11:45:26 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 	NET_ASSERT_LOCKED();
399 
400 	if (m == NULL) {
401 		DPRINTF("bogus returned buffer from crypto");
402 		ipsecstat_inc(ipsec_crypto);
403 		goto drop;
404 	}
405 
406 	tdb = gettdb(tc->tc_rdomain, tc->tc_spi, &tc->tc_dst, tc->tc_proto);
407 	if (tdb == NULL) {
408 		DPRINTF("TDB is expired while in crypto");
409 		ipsecstat_inc(ipsec_notdb);
410 		goto drop;
411 	}
412 
413 	/* Check for crypto errors. */
414 	if (crp->crp_etype) {
415 		if (crp->crp_etype == EAGAIN) {
416 			/* Reset the session ID */
417 			if (tdb->tdb_cryptoid != 0)
418 				tdb->tdb_cryptoid = crp->crp_sid;
419 			error = crypto_dispatch(crp);
420 			if (error) {
421 				DPRINTF("crypto dispatch error %d", error);
422 				goto drop;
423 			}
424 			return;
425 		}
426 		DPRINTF("crypto error %d", crp->crp_etype);
427 		ipsecstat_inc(ipsec_noxform);
428 		goto drop;
429 	}
430 
431 	olen = crp->crp_olen;
432 	ilen = crp->crp_ilen;
433 
434 	/* Release crypto descriptors. */
435 	crypto_freereq(crp);
436 
437 	switch (tdb->tdb_sproto) {
438 	case IPPROTO_ESP:
439 		error = esp_output_cb(tdb, tc, m, ilen, olen);
440 		break;
441 	case IPPROTO_AH:
442 		error = ah_output_cb(tdb, tc, m, ilen, olen);
443 		break;
444 	case IPPROTO_IPCOMP:
445 		error = ipcomp_output_cb(tdb, tc, m, ilen, olen);
446 		break;
447 	default:
448 		panic("%s: unknown/unsupported security protocol %d",
449 		    __func__, tdb->tdb_sproto);
450 	}
451 
452 	if (error) {
453 		ipsecstat_inc(ipsec_odrops);
454 		tdb->tdb_odrops++;
455 	}
456 	return;
457 
458  drop:
459 	if (tdb != NULL)
460 		tdb->tdb_odrops++;
461 	m_freem(m);
462 	free(tc, M_XDATA, 0);
463 	crypto_freereq(crp);
464 	ipsecstat_inc(ipsec_odrops);
465 }
466 
467 /*
468  * Called by the IPsec output transform callbacks, to transmit the packet
469  * or do further processing, as necessary.
470  */
471 int
472 ipsp_process_done(struct mbuf *m, struct tdb *tdb)
473 {
474 	struct ip *ip;
475 #ifdef INET6
476 	struct ip6_hdr *ip6;
477 #endif /* INET6 */
478 	struct tdb_ident *tdbi;
479 	struct m_tag *mtag;
480 	int roff, error;
481 
482 	NET_ASSERT_LOCKED();
483 
484 	tdb->tdb_last_used = gettime();
485 
486 	if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0) {
487 		struct mbuf *mi;
488 		struct udphdr *uh;
489 		int iphlen;
490 
491 		if (!udpencap_enable || !udpencap_port) {
492 			error = ENXIO;
493 			goto drop;
494 		}
495 
496 		switch (tdb->tdb_dst.sa.sa_family) {
497 		case AF_INET:
498 			iphlen = sizeof(struct ip);
499 			break;
500 #ifdef INET6
501 		case AF_INET6:
502 			iphlen = sizeof(struct ip6_hdr);
503 			break;
504 #endif /* INET6 */
505 		default:
506 			DPRINTF("unknown protocol family (%d)",
507 			    tdb->tdb_dst.sa.sa_family);
508 			error = ENXIO;
509 			goto drop;
510 		}
511 
512 		mi = m_makespace(m, iphlen, sizeof(struct udphdr), &roff);
513 		if (mi == NULL) {
514 			error = ENOMEM;
515 			goto drop;
516 		}
517 		uh = (struct udphdr *)(mtod(mi, caddr_t) + roff);
518 		uh->uh_sport = uh->uh_dport = htons(udpencap_port);
519 		if (tdb->tdb_udpencap_port)
520 			uh->uh_dport = tdb->tdb_udpencap_port;
521 
522 		uh->uh_ulen = htons(m->m_pkthdr.len - iphlen);
523 		uh->uh_sum = 0;
524 #ifdef INET6
525 		if (tdb->tdb_dst.sa.sa_family == AF_INET6)
526 			m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
527 #endif /* INET6 */
528 		espstat_inc(esps_udpencout);
529 	}
530 
531 	switch (tdb->tdb_dst.sa.sa_family) {
532 	case AF_INET:
533 		/* Fix the header length, for AH processing. */
534 		ip = mtod(m, struct ip *);
535 		ip->ip_len = htons(m->m_pkthdr.len);
536 		if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0)
537 			ip->ip_p = IPPROTO_UDP;
538 		break;
539 
540 #ifdef INET6
541 	case AF_INET6:
542 		/* Fix the header length, for AH processing. */
543 		if (m->m_pkthdr.len < sizeof(*ip6)) {
544 			error = ENXIO;
545 			goto drop;
546 		}
547 		if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) {
548 			/* No jumbogram support. */
549 			error = ENXIO;
550 			goto drop;
551 		}
552 		ip6 = mtod(m, struct ip6_hdr *);
553 		ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6));
554 		if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0)
555 			ip6->ip6_nxt = IPPROTO_UDP;
556 		break;
557 #endif /* INET6 */
558 
559 	default:
560 		DPRINTF("unknown protocol family (%d)",
561 		    tdb->tdb_dst.sa.sa_family);
562 		error = ENXIO;
563 		goto drop;
564 	}
565 
566 	/*
567 	 * Add a record of what we've done or what needs to be done to the
568 	 * packet.
569 	 */
570 	mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, sizeof(struct tdb_ident),
571 	    M_NOWAIT);
572 	if (mtag == NULL) {
573 		DPRINTF("could not allocate packet tag");
574 		error = ENOMEM;
575 		goto drop;
576 	}
577 
578 	tdbi = (struct tdb_ident *)(mtag + 1);
579 	tdbi->dst = tdb->tdb_dst;
580 	tdbi->proto = tdb->tdb_sproto;
581 	tdbi->spi = tdb->tdb_spi;
582 	tdbi->rdomain = tdb->tdb_rdomain;
583 
584 	m_tag_prepend(m, mtag);
585 
586 	ipsecstat_pkt(ipsec_opackets, ipsec_obytes, m->m_pkthdr.len);
587 	tdb->tdb_opackets++;
588 	tdb->tdb_obytes += m->m_pkthdr.len;
589 
590 	/* If there's another (bundled) TDB to apply, do so. */
591 	if (tdb->tdb_onext)
592 		return ipsp_process_packet(m, tdb->tdb_onext,
593 		    tdb->tdb_dst.sa.sa_family, 0);
594 
595 #if NPF > 0
596 	/* Add pf tag if requested. */
597 	pf_tag_packet(m, tdb->tdb_tag, -1);
598 	pf_pkt_addr_changed(m);
599 #endif
600 	if (tdb->tdb_rdomain != tdb->tdb_rdomain_post)
601 		m->m_pkthdr.ph_rtableid = tdb->tdb_rdomain_post;
602 
603 	/*
604 	 * We're done with IPsec processing, transmit the packet using the
605 	 * appropriate network protocol (IP or IPv6). SPD lookup will be
606 	 * performed again there.
607 	 */
608 	switch (tdb->tdb_dst.sa.sa_family) {
609 	case AF_INET:
610 		return (ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL, 0));
611 
612 #ifdef INET6
613 	case AF_INET6:
614 		/*
615 		 * We don't need massage, IPv6 header fields are always in
616 		 * net endian.
617 		 */
618 		return (ip6_output(m, NULL, NULL, 0, NULL, NULL));
619 #endif /* INET6 */
620 	}
621 	error = EINVAL; /* Not reached. */
622 
623  drop:
624 	m_freem(m);
625 	return error;
626 }
627 
628 ssize_t
629 ipsec_hdrsz(struct tdb *tdbp)
630 {
631 	ssize_t adjust;
632 
633 	switch (tdbp->tdb_sproto) {
634 	case IPPROTO_IPIP:
635 		adjust = 0;
636 		break;
637 
638 	case IPPROTO_ESP:
639 		if (tdbp->tdb_encalgxform == NULL)
640 			return (-1);
641 
642 		/* Header length */
643 		adjust = 2 * sizeof(u_int32_t) + tdbp->tdb_ivlen;
644 		if (tdbp->tdb_flags & TDBF_UDPENCAP)
645 			adjust += sizeof(struct udphdr);
646 		/* Authenticator */
647 		if (tdbp->tdb_authalgxform != NULL)
648 			adjust += tdbp->tdb_authalgxform->authsize;
649 		/* Padding */
650 		adjust += MAX(4, tdbp->tdb_encalgxform->blocksize);
651 		break;
652 
653 	case IPPROTO_AH:
654 		if (tdbp->tdb_authalgxform == NULL)
655 			return (-1);
656 
657 		adjust = AH_FLENGTH + sizeof(u_int32_t);
658 		adjust += tdbp->tdb_authalgxform->authsize;
659 		break;
660 
661 	default:
662 		return (-1);
663 	}
664 
665 	if (!(tdbp->tdb_flags & TDBF_TUNNELING) &&
666 	    !(tdbp->tdb_flags & TDBF_USEDTUNNEL))
667 		return (adjust);
668 
669 	switch (tdbp->tdb_dst.sa.sa_family) {
670 	case AF_INET:
671 		adjust += sizeof(struct ip);
672 		break;
673 #ifdef INET6
674 	case AF_INET6:
675 		adjust += sizeof(struct ip6_hdr);
676 		break;
677 #endif /* INET6 */
678 	}
679 
680 	return (adjust);
681 }
682 
683 void
684 ipsec_adjust_mtu(struct mbuf *m, u_int32_t mtu)
685 {
686 	struct tdb_ident *tdbi;
687 	struct tdb *tdbp;
688 	struct m_tag *mtag;
689 	ssize_t adjust;
690 
691 	NET_ASSERT_LOCKED();
692 
693 	for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL); mtag;
694 	     mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, mtag)) {
695 		tdbi = (struct tdb_ident *)(mtag + 1);
696 		tdbp = gettdb(tdbi->rdomain, tdbi->spi, &tdbi->dst,
697 		    tdbi->proto);
698 		if (tdbp == NULL)
699 			break;
700 
701 		if ((adjust = ipsec_hdrsz(tdbp)) == -1)
702 			break;
703 
704 		mtu -= adjust;
705 		tdbp->tdb_mtu = mtu;
706 		tdbp->tdb_mtutimeout = gettime() + ip_mtudisc_timeout;
707 		DPRINTF("spi %08x mtu %d adjust %ld mbuf %p",
708 		    ntohl(tdbp->tdb_spi), tdbp->tdb_mtu, adjust, m);
709 	}
710 }
711