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