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