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