xref: /openbsd-src/sys/netinet/ipsec_output.c (revision 3a3fbb3f2e2521ab7c4a56b7ff7462ebd9095ec5)
1 /*	$OpenBSD: ipsec_output.c,v 1.20 2001/12/06 22:52:10 angelos 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 <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/mbuf.h>
26 #include <sys/socket.h>
27 #include <sys/kernel.h>
28 
29 #include <net/if.h>
30 #include <net/route.h>
31 
32 #ifdef INET
33 #include <netinet/in.h>
34 #include <netinet/in_systm.h>
35 #include <netinet/ip.h>
36 #include <netinet/in_pcb.h>
37 #include <netinet/ip_var.h>
38 #endif /* INET */
39 
40 #ifdef INET6
41 #ifndef INET
42 #include <netinet/in.h>
43 #endif
44 #include <netinet6/in6_var.h>
45 #endif /* INET6 */
46 
47 #include <netinet/ip_ipsp.h>
48 #include <netinet/ip_ah.h>
49 #include <netinet/ip_esp.h>
50 #include <netinet/ip_ipcomp.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 /*
60  * Loop over a tdb chain, taking into consideration protocol tunneling. The
61  * fourth argument is set if the first encapsulation header is already in
62  * place.
63  */
64 int
65 ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready)
66 {
67 	struct timeval tv;
68 	int i, off, error;
69 	struct mbuf *mp;
70 
71 #ifdef INET
72 	int setdf = 0;
73 	struct ip *ip;
74 #endif /* INET */
75 #ifdef INET6
76 	struct ip6_hdr *ip6;
77 #endif /* INET6 */
78 
79 	/* Check that the transform is allowed by the administrator. */
80 	if ((tdb->tdb_sproto == IPPROTO_ESP && !esp_enable) ||
81 	    (tdb->tdb_sproto == IPPROTO_AH && !ah_enable) ||
82 	    (tdb->tdb_sproto == IPPROTO_IPCOMP && !ipcomp_enable)) {
83 		DPRINTF(("ipsp_process_packet(): IPSec outbound packet "
84 		    "dropped due to policy (check your sysctls)\n"));
85 		m_freem(m);
86 		return EHOSTUNREACH;
87 	}
88 
89 	/* Sanity check. */
90 	if (!tdb->tdb_xform) {
91 		DPRINTF(("ipsp_process_packet(): uninitialized TDB\n"));
92 		m_freem(m);
93 		return EHOSTUNREACH;
94 	}
95 
96 	/* Check if the SPI is invalid. */
97 	if (tdb->tdb_flags & TDBF_INVALID) {
98 		DPRINTF(("ipsp_process_packet(): attempt to use invalid "
99 		    "SA %s/%08x/%u\n", ipsp_address(tdb->tdb_dst),
100 		    ntohl(tdb->tdb_spi), tdb->tdb_sproto));
101 		m_freem(m);
102 		return ENXIO;
103 	}
104 
105 	/* Check that the network protocol is supported */
106 	switch (tdb->tdb_dst.sa.sa_family) {
107 #ifdef INET
108 	case AF_INET:
109 		break;
110 #endif /* INET */
111 
112 #ifdef INET6
113 	case AF_INET6:
114 		break;
115 #endif /* INET6 */
116 
117 	default:
118 		DPRINTF(("ipsp_process_packet(): attempt to use "
119 		    "SA %s/%08x/%u for protocol family %d\n",
120 		    ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi),
121 		    tdb->tdb_sproto, tdb->tdb_dst.sa.sa_family));
122 		m_freem(m);
123 		return ENXIO;
124 	}
125 
126 	/*
127 	 * Register first use if applicable, setup relevant expiration timer.
128 	 */
129 	if (tdb->tdb_first_use == 0) {
130 		int pri;
131 
132 		pri = splhigh();
133 		tdb->tdb_first_use = time.tv_sec;
134 		splx(pri);
135 
136 		tv.tv_usec = 0;
137 
138 		tv.tv_sec = tdb->tdb_first_use + tdb->tdb_exp_first_use;
139 		if (tdb->tdb_flags & TDBF_FIRSTUSE)
140 			timeout_add(&tdb->tdb_first_tmo,
141 			    hzto(&tv));
142 
143 		tv.tv_sec = tdb->tdb_first_use + tdb->tdb_soft_first_use;
144 		if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE)
145 			timeout_add(&tdb->tdb_sfirst_tmo,
146 			    hzto(&tv));
147 	}
148 
149 	/*
150 	 * Check for tunneling if we don't have the first header in place.
151 	 * When doing Ethernet-over-IP, we are handed an already-encapsulated
152 	 * frame, so we don't need to re-encapsulate.
153 	 */
154 	if (tunalready == 0) {
155 		/*
156 		 * If the target protocol family is different, we know we'll be
157 		 * doing tunneling.
158 		 */
159 		if (af == tdb->tdb_dst.sa.sa_family) {
160 #ifdef INET
161 			if (af == AF_INET)
162 				i = sizeof(struct ip);
163 #endif /* INET */
164 
165 #ifdef INET6
166 			if (af == AF_INET6)
167 				i = sizeof(struct ip6_hdr);
168 #endif /* INET6 */
169 
170 			/* Bring the network header in the first mbuf. */
171 			if (m->m_len < i) {
172 				if ((m = m_pullup(m, i)) == NULL)
173 					return ENOBUFS;
174 			}
175 
176 #ifdef INET
177 			ip = mtod(m, struct ip *);
178 
179 			/*
180 			 * This is not a bridge packet, remember if we
181 			 * had IP_DF.
182 			 */
183 			setdf = ntohs(ip->ip_off) & IP_DF;
184 #endif /* INET */
185 
186 #ifdef INET6
187 			ip6 = mtod(m, struct ip6_hdr *);
188 #endif /* INET6 */
189 		}
190 
191 		/* Do the appropriate encapsulation, if necessary. */
192 		if ((tdb->tdb_dst.sa.sa_family != af) || /* PF mismatch */
193 		    (tdb->tdb_flags & TDBF_TUNNELING) || /* Tunneling needed */
194 		    (tdb->tdb_xform->xf_type == XF_IP4) || /* ditto */
195 #ifdef INET
196 		    ((tdb->tdb_dst.sa.sa_family == AF_INET) &&
197 			(tdb->tdb_dst.sin.sin_addr.s_addr != INADDR_ANY) &&
198 			(tdb->tdb_dst.sin.sin_addr.s_addr != ip->ip_dst.s_addr)) ||
199 #endif /* INET */
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 #ifdef INET
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 						return ENOBUFS;
214 
215 				ip = mtod(m, struct ip *);
216 				ip->ip_len = htons(m->m_pkthdr.len);
217 				ip->ip_sum = 0;
218 				ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
219 			}
220 #endif /* INET */
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 						return ENOBUFS;
229 
230 				if (m->m_pkthdr.len - sizeof(*ip6) >
231 				    IPV6_MAXPACKET) {
232 					/* No jumbogram support. */
233 					m_freem(m);
234 					return ENXIO;	/*?*/
235 				}
236 				ip6 = mtod(m, struct ip6_hdr *);
237 				ip6->ip6_plen = htons(m->m_pkthdr.len
238 				    - sizeof(*ip6));
239 			}
240 #endif /* INET6 */
241 
242 			/* Encapsulate -- the last two arguments are unused. */
243 			error = ipip_output(m, tdb, &mp, 0, 0);
244 			if ((mp == NULL) && (!error))
245 				error = EFAULT;
246 			if (error) {
247 				if (mp)	{
248 					m_freem(mp);
249 					mp = NULL;
250 				}
251 				return error;
252 			}
253 
254 			m = mp;
255 			mp = NULL;
256 
257 #ifdef INET
258 			if (tdb->tdb_dst.sa.sa_family == AF_INET && setdf) {
259 				ip = mtod(m, struct ip *);
260 				if (m->m_len < sizeof(struct ip))
261 					if ((m = m_pullup(m,
262 					    sizeof(struct ip))) == NULL)
263 						return ENOBUFS;
264 
265 				NTOHS(ip->ip_off);
266 				ip->ip_off |= IP_DF;
267 				HTONS(ip->ip_off);
268 			}
269 
270 			/* Remember that we appended a tunnel header. */
271 			tdb->tdb_flags |= TDBF_USEDTUNNEL;
272 #endif
273 		}
274 
275 		/* We may be done with this TDB */
276 		if (tdb->tdb_xform->xf_type == XF_IP4)
277 			return ipsp_process_done(m, tdb);
278 	} else {
279 		/*
280 		 * If this is just an IP-IP TDB and we're told there's
281 		 * already an encapsulation header, move on.
282 		 */
283 		if (tdb->tdb_xform->xf_type == XF_IP4)
284 			return ipsp_process_done(m, tdb);
285 	}
286 
287 	/* Extract some information off the headers. */
288 	switch (tdb->tdb_dst.sa.sa_family) {
289 #ifdef INET
290 	case AF_INET:
291 		ip = mtod(m, struct ip *);
292 		i = ip->ip_hl << 2;
293 		off = offsetof(struct ip, ip_p);
294 		break;
295 #endif /* INET */
296 
297 #ifdef INET6
298 	case AF_INET6:
299 		ip6 = mtod(m, struct ip6_hdr *);
300 		i = sizeof(struct ip6_hdr);
301 		off = offsetof(struct ip6_hdr, ip6_nxt);
302 		break;
303 #endif /* INET6 */
304 	}
305 
306 	/* Non expansion policy for IPCOMP */
307 	if (tdb->tdb_sproto == IPPROTO_IPCOMP) {
308 		if ((m->m_pkthdr.len - i) < tdb->tdb_compalgxform->minlen) {
309 			/* No need to compress, leave the packet untouched */
310 			return ipsp_process_done(m, tdb);
311 		}
312 	}
313 
314 	/* Invoke the IPsec transform. */
315 	return (*(tdb->tdb_xform->xf_output))(m, tdb, NULL, i, off);
316 }
317 
318 /*
319  * Called by the IPsec output transform callbacks, to transmit the packet
320  * or do further processing, as necessary.
321  */
322 int
323 ipsp_process_done(struct mbuf *m, struct tdb *tdb)
324 {
325 #ifdef INET
326 	struct ip *ip;
327 #endif /* INET */
328 
329 #ifdef INET6
330 	struct ip6_hdr *ip6;
331 #endif /* INET6 */
332 
333 	struct tdb_ident *tdbi;
334 	struct m_tag *mtag;
335 
336 	tdb->tdb_last_used = time.tv_sec;
337 
338 	switch (tdb->tdb_dst.sa.sa_family) {
339 #ifdef INET
340 	case AF_INET:
341 		/* Fix the header length, for AH processing. */
342 		if (tdb->tdb_dst.sa.sa_family == AF_INET) {
343 			ip = mtod(m, struct ip *);
344 			ip->ip_len = htons(m->m_pkthdr.len);
345 		}
346 		break;
347 #endif /* INET */
348 
349 #ifdef INET6
350 	case AF_INET6:
351 		/* Fix the header length, for AH processing. */
352 		if (tdb->tdb_dst.sa.sa_family == AF_INET6) {
353 			if (m->m_pkthdr.len < sizeof(*ip6)) {
354 				m_freem(m);
355 				return ENXIO;
356 			}
357 			if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) {
358 				/* No jumbogram support. */
359 				m_freem(m);
360 				return ENXIO;	/*?*/
361 			}
362 			ip6 = mtod(m, struct ip6_hdr *);
363 			ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6));
364 		}
365 		break;
366 #endif /* INET6 */
367 
368 	default:
369 		m_freem(m);
370 		DPRINTF(("ipsp_process_done(): unknown protocol family (%d)\n",
371 		    tdb->tdb_dst.sa.sa_family));
372 		return ENXIO;
373 	}
374 
375 	/*
376 	 * Add a record of what we've done or what needs to be done to the
377 	 * packet.
378 	 */
379 	if ((tdb->tdb_flags & TDBF_SKIPCRYPTO) == 0)
380 		mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE,
381 		    sizeof(struct tdb_ident),
382 		    M_NOWAIT);
383 	else
384 		mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED,
385 		    sizeof(struct tdb_ident), M_NOWAIT);
386 
387 	if (mtag == NULL) {
388 		m_freem(m);
389 		DPRINTF(("ipsp_process_done(): could not allocate packet "
390 		    "tag\n"));
391 		return ENOMEM;
392 	}
393 
394 	tdbi = (struct tdb_ident *)(mtag + 1);
395 	bcopy(&tdb->tdb_dst, &tdbi->dst, sizeof(union sockaddr_union));
396 	tdbi->proto = tdb->tdb_sproto;
397 	tdbi->spi = tdb->tdb_spi;
398 
399 	m_tag_prepend(m, mtag);
400 
401 	/* If there's another (bundled) TDB to apply, do so. */
402 	if (tdb->tdb_onext)
403 		return ipsp_process_packet(m, tdb->tdb_onext,
404 		    tdb->tdb_dst.sa.sa_family, 0);
405 
406 	/*
407 	 * We're done with IPsec processing, transmit the packet using the
408 	 * appropriate network protocol (IP or IPv6). SPD lookup will be
409 	 * performed again there.
410 	 */
411 	switch (tdb->tdb_dst.sa.sa_family) {
412 #ifdef INET
413 	case AF_INET:
414 		NTOHS(ip->ip_len);
415 		NTOHS(ip->ip_off);
416 
417 		return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL);
418 #endif /* INET */
419 
420 #ifdef INET6
421 	case AF_INET6:
422 		/*
423 		 * We don't need massage, IPv6 header fields are always in
424 		 * net endian.
425 		 */
426 		return ip6_output(m, NULL, NULL, 0, NULL, NULL);
427 #endif /* INET6 */
428 	}
429 	return EINVAL; /* Not reached. */
430 }
431 
432 ssize_t
433 ipsec_hdrsz(struct tdb *tdbp)
434 {
435 	ssize_t adjust;
436 
437 	switch (tdbp->tdb_sproto) {
438 	case IPPROTO_ESP:
439 		if (tdbp->tdb_encalgxform == NULL)
440 			return (-1);
441 
442 		/* Header length */
443 		if (tdbp->tdb_flags & TDBF_NOREPLAY)
444 			adjust = sizeof(u_int32_t) + tdbp->tdb_ivlen;
445 		else
446 			adjust = 2 * sizeof(u_int32_t) + tdbp->tdb_ivlen;
447 		/* Authenticator */
448 		if (tdbp->tdb_authalgxform != NULL)
449 			adjust += AH_HMAC_HASHLEN;
450 		/* Padding */
451 		adjust += tdbp->tdb_encalgxform->blocksize;
452 		break;
453 
454 	case IPPROTO_AH:
455 		if (tdbp->tdb_authalgxform == NULL)
456 			return (-1);
457 
458 		if (!(tdbp->tdb_flags & TDBF_NOREPLAY))
459 			adjust = AH_FLENGTH + sizeof(u_int32_t);
460 		else
461 			adjust = AH_FLENGTH;
462 		adjust += tdbp->tdb_authalgxform->authsize;
463 		break;
464 
465 	default:
466 		return (-1);
467 	}
468 
469 	if (!(tdbp->tdb_flags & TDBF_TUNNELING) &&
470 	    !(tdbp->tdb_flags & TDBF_USEDTUNNEL))
471 		return (adjust);
472 
473 	switch (tdbp->tdb_dst.sa.sa_family) {
474 #ifdef INET
475 	case AF_INET:
476 		adjust += sizeof(struct ip);
477 		break;
478 #endif /* INET */
479 #ifdef INET6
480 	case AF_INET6:
481 		adjust += sizeof(struct ip6_hdr);
482 		break;
483 #endif /* INET6 */
484 	}
485 
486 	return (adjust);
487 }
488 
489 void
490 ipsec_adjust_mtu(struct mbuf *m, u_int32_t mtu)
491 {
492 	struct tdb_ident *tdbi;
493 	struct tdb *tdbp;
494 	struct m_tag *mtag;
495 	ssize_t adjust;
496 	int s;
497 
498 	s = spltdb();
499 
500 	for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL); mtag;
501 	     mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, mtag)) {
502 		tdbi = (struct tdb_ident *)(mtag + 1);
503 		tdbp = gettdb(tdbi->spi, &tdbi->dst, tdbi->proto);
504 		if (tdbp == NULL)
505 			break;
506 
507 		if ((adjust = ipsec_hdrsz(tdbp)) == -1)
508 			break;
509 
510 		mtu -= adjust;
511 		tdbp->tdb_mtu = mtu;
512 		tdbp->tdb_mtutimeout = time.tv_sec + ip_mtudisc_timeout;
513 	}
514 
515 	splx(s);
516 }
517