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