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