Annotation of src/sys/netipsec/ipsec_output.c, Revision 1.5
1.5 ! itojun 1: /* $NetBSD: ipsec_output.c,v 1.4 2003/08/20 22:33:40 jonathan Exp $ */
1.1 jonathan 2: /* $FreeBSD: src/sys/netipsec/ipsec_output.c,v 1.3.2.1 2003/01/24 05:11:35 sam Exp $ */
3: /* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane Exp $ */
4:
5: #include <sys/cdefs.h>
1.5 ! itojun 6: __KERNEL_RCSID(0, "$NetBSD: ipsec_output.c,v 1.4 2003/08/20 22:33:40 jonathan Exp $");
1.1 jonathan 7:
8: /*
9: * IPsec output processing.
10: */
11: #include "opt_inet.h"
1.4 jonathan 12: #ifdef __FreeBSD__
1.1 jonathan 13: #include "opt_inet6.h"
1.4 jonathan 14: #endif
1.1 jonathan 15: #include "opt_ipsec.h"
16:
17: #include <sys/param.h>
18: #include <sys/systm.h>
19: #include <sys/mbuf.h>
20: #include <sys/domain.h>
21: #include <sys/protosw.h>
22: #include <sys/socket.h>
23: #include <sys/errno.h>
24: #include <sys/syslog.h>
25:
26: #include <net/if.h>
27: #include <net/route.h>
28:
29: #include <netinet/in.h>
30: #include <netinet/in_systm.h>
31: #include <netinet/ip.h>
32: #include <netinet/ip_var.h>
33: #include <netinet/in_var.h>
34: #include <netinet/ip_ecn.h>
35: #ifdef INET6
36: #include <netinet6/ip6_ecn.h>
37: #endif
38:
39: #include <netinet/ip6.h>
40: #ifdef INET6
41: #include <netinet6/ip6_var.h>
42: #endif
43: #include <netinet/in_pcb.h>
44: #ifdef INET6
45: #include <netinet/icmp6.h>
46: #endif
47:
48: #include <netipsec/ipsec.h>
49: #ifdef INET6
50: #include <netipsec/ipsec6.h>
51: #endif
52: #include <netipsec/ah_var.h>
53: #include <netipsec/esp_var.h>
54: #include <netipsec/ipcomp_var.h>
55:
56: #include <netipsec/xform.h>
57:
58: #include <netipsec/key.h>
59: #include <netipsec/keydb.h>
60: #include <netipsec/key_debug.h>
61: #include <netipsec/ipsec_osdep.h>
62:
63: int
64: ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
65: {
66: struct tdb_ident *tdbi;
67: struct m_tag *mtag;
68: struct secasvar *sav;
69: struct secasindex *saidx;
70: int error;
71:
72: IPSEC_SPLASSERT_SOFTNET("ipsec_process_done");
73:
74: IPSEC_ASSERT(m != NULL, ("ipsec_process_done: null mbuf"));
75: IPSEC_ASSERT(isr != NULL, ("ipsec_process_done: null ISR"));
76: sav = isr->sav;
77: IPSEC_ASSERT(sav != NULL, ("ipsec_process_done: null SA"));
78: IPSEC_ASSERT(sav->sah != NULL, ("ipsec_process_done: null SAH"));
79:
80: saidx = &sav->sah->saidx;
81: switch (saidx->dst.sa.sa_family) {
82: #ifdef INET
83: case AF_INET:
84: /* Fix the header length, for AH processing. */
85: mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len);
86: break;
87: #endif /* INET */
88: #ifdef INET6
89: case AF_INET6:
90: /* Fix the header length, for AH processing. */
91: if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) {
92: error = ENXIO;
93: goto bad;
94: }
95: if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) {
96: /* No jumbogram support. */
97: error = ENXIO; /*?*/
98: goto bad;
99: }
100: mtod(m, struct ip6_hdr *)->ip6_plen =
101: htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
102: break;
103: #endif /* INET6 */
104: default:
105: DPRINTF(("ipsec_process_done: unknown protocol family %u\n",
106: saidx->dst.sa.sa_family));
107: error = ENXIO;
108: goto bad;
109: }
110:
111: /*
112: * Add a record of what we've done or what needs to be done to the
113: * packet.
114: */
115: mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE,
116: sizeof(struct tdb_ident), M_NOWAIT);
117: if (mtag == NULL) {
118: DPRINTF(("ipsec_process_done: could not get packet tag\n"));
119: error = ENOMEM;
120: goto bad;
121: }
122:
123: tdbi = (struct tdb_ident *)(mtag + 1);
124: tdbi->dst = saidx->dst;
125: tdbi->proto = saidx->proto;
126: tdbi->spi = sav->spi;
127: m_tag_prepend(m, mtag);
128:
129: /*
130: * If there's another (bundled) SA to apply, do so.
131: * Note that this puts a burden on the kernel stack size.
132: * If this is a problem we'll need to introduce a queue
133: * to set the packet on so we can unwind the stack before
134: * doing further processing.
135: */
136: if (isr->next) {
137: newipsecstat.ips_out_bundlesa++;
138: return ipsec4_process_packet(m, isr->next, 0, 0);
139: }
140:
141: /*
142: * We're done with IPsec processing, transmit the packet using the
143: * appropriate network protocol (IP or IPv6). SPD lookup will be
144: * performed again there.
145: */
146: switch (saidx->dst.sa.sa_family) {
147: #ifdef INET
148: struct ip *ip;
149: case AF_INET:
150: ip = mtod(m, struct ip *);
151: #ifdef __FreeBSD__
152: /* FreeBSD ip_output() expects ip_len, ip_off in host endian */
153: ip->ip_len = ntohs(ip->ip_len);
154: ip->ip_off = ntohs(ip->ip_off);
155: #endif /* __FreeBSD_ */
1.2 jonathan 156: return ip_output(m, NULL, NULL, IP_RAWOUTPUT,
1.5 ! itojun 157: (struct ip_moptions *)NULL, (struct socket *)NULL);
1.2 jonathan 158:
1.1 jonathan 159: #endif /* INET */
160: #ifdef INET6
161: case AF_INET6:
162: /*
163: * We don't need massage, IPv6 header fields are always in
164: * net endian.
165: */
166: return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL);
167: #endif /* INET6 */
168: }
169: panic("ipsec_process_done");
170: bad:
171: m_freem(m);
172: KEY_FREESAV(&sav);
173: return (error);
174: }
175:
176: static struct ipsecrequest *
177: ipsec_nextisr(
178: struct mbuf *m,
179: struct ipsecrequest *isr,
180: int af,
181: struct secasindex *saidx,
182: int *error
183: )
184: {
185: #define IPSEC_OSTAT(x,y,z) (isr->saidx.proto == IPPROTO_ESP ? (x)++ : \
186: isr->saidx.proto == IPPROTO_AH ? (y)++ : (z)++)
187: struct secasvar *sav;
188:
189: IPSEC_SPLASSERT_SOFTNET("ipsec_nextisr");
190: IPSEC_ASSERT(af == AF_INET || af == AF_INET6,
191: ("ipsec_nextisr: invalid address family %u", af));
192: again:
193: /*
194: * Craft SA index to search for proper SA. Note that
195: * we only fillin unspecified SA peers for transport
196: * mode; for tunnel mode they must already be filled in.
197: */
198: *saidx = isr->saidx;
199: if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
200: /* Fillin unspecified SA peers only for transport mode */
201: if (af == AF_INET) {
202: struct sockaddr_in *sin;
203: struct ip *ip = mtod(m, struct ip *);
204:
205: if (saidx->src.sa.sa_len == 0) {
206: sin = &saidx->src.sin;
207: sin->sin_len = sizeof(*sin);
208: sin->sin_family = AF_INET;
209: sin->sin_port = IPSEC_PORT_ANY;
210: sin->sin_addr = ip->ip_src;
211: }
212: if (saidx->dst.sa.sa_len == 0) {
213: sin = &saidx->dst.sin;
214: sin->sin_len = sizeof(*sin);
215: sin->sin_family = AF_INET;
216: sin->sin_port = IPSEC_PORT_ANY;
217: sin->sin_addr = ip->ip_dst;
218: }
219: } else {
220: struct sockaddr_in6 *sin6;
221: struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
222:
223: if (saidx->src.sin6.sin6_len == 0) {
224: sin6 = (struct sockaddr_in6 *)&saidx->src;
225: sin6->sin6_len = sizeof(*sin6);
226: sin6->sin6_family = AF_INET6;
227: sin6->sin6_port = IPSEC_PORT_ANY;
228: sin6->sin6_addr = ip6->ip6_src;
229: if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
230: /* fix scope id for comparing SPD */
231: sin6->sin6_addr.s6_addr16[1] = 0;
232: sin6->sin6_scope_id =
233: ntohs(ip6->ip6_src.s6_addr16[1]);
234: }
235: }
236: if (saidx->dst.sin6.sin6_len == 0) {
237: sin6 = (struct sockaddr_in6 *)&saidx->dst;
238: sin6->sin6_len = sizeof(*sin6);
239: sin6->sin6_family = AF_INET6;
240: sin6->sin6_port = IPSEC_PORT_ANY;
241: sin6->sin6_addr = ip6->ip6_dst;
242: if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
243: /* fix scope id for comparing SPD */
244: sin6->sin6_addr.s6_addr16[1] = 0;
245: sin6->sin6_scope_id =
246: ntohs(ip6->ip6_dst.s6_addr16[1]);
247: }
248: }
249: }
250: }
251:
252: /*
253: * Lookup SA and validate it.
254: */
255: *error = key_checkrequest(isr, saidx);
256: if (*error != 0) {
257: /*
258: * IPsec processing is required, but no SA found.
259: * I assume that key_acquire() had been called
260: * to get/establish the SA. Here I discard
261: * this packet because it is responsibility for
262: * upper layer to retransmit the packet.
263: */
264: newipsecstat.ips_out_nosa++;
265: goto bad;
266: }
267: sav = isr->sav;
268: if (sav == NULL) { /* XXX valid return */
269: IPSEC_ASSERT(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE,
270: ("ipsec_nextisr: no SA found, but required; level %u",
271: ipsec_get_reqlevel(isr)));
272: isr = isr->next;
273: if (isr == NULL) {
274: /*XXXstatistic??*/
275: *error = EINVAL; /*XXX*/
276: return isr;
277: }
278: goto again;
279: }
280:
281: /*
282: * Check system global policy controls.
283: */
284: if ((isr->saidx.proto == IPPROTO_ESP && !esp_enable) ||
285: (isr->saidx.proto == IPPROTO_AH && !ah_enable) ||
286: (isr->saidx.proto == IPPROTO_IPCOMP && !ipcomp_enable)) {
287: DPRINTF(("ipsec_nextisr: IPsec outbound packet dropped due"
288: " to policy (check your sysctls)\n"));
289: IPSEC_OSTAT(espstat.esps_pdrops, ahstat.ahs_pdrops,
290: ipcompstat.ipcomps_pdrops);
291: *error = EHOSTUNREACH;
292: goto bad;
293: }
294:
295: /*
296: * Sanity check the SA contents for the caller
297: * before they invoke the xform output method.
298: */
299: if (sav->tdb_xform == NULL) {
300: DPRINTF(("ipsec_nextisr: no transform for SA\n"));
301: IPSEC_OSTAT(espstat.esps_noxform, ahstat.ahs_noxform,
302: ipcompstat.ipcomps_noxform);
303: *error = EHOSTUNREACH;
304: goto bad;
305: }
306: return isr;
307: bad:
308: IPSEC_ASSERT(*error != 0, ("ipsec_nextisr: error return w/ no error code"));
309: return NULL;
310: #undef IPSEC_OSTAT
311: }
312:
313: #ifdef INET
314: /*
315: * IPsec output logic for IPv4.
316: */
317: int
318: ipsec4_process_packet(
319: struct mbuf *m,
320: struct ipsecrequest *isr,
321: int flags,
322: int tunalready)
323: {
324: struct secasindex saidx;
325: struct secasvar *sav;
326: struct ip *ip;
327: int s, error, i, off;
328:
329: IPSEC_ASSERT(m != NULL, ("ipsec4_process_packet: null mbuf"));
330: IPSEC_ASSERT(isr != NULL, ("ipsec4_process_packet: null isr"));
331:
332: s = splsoftnet(); /* insure SA contents don't change */
333:
334: isr = ipsec_nextisr(m, isr, AF_INET, &saidx, &error);
335: if (isr == NULL)
336: goto bad;
337:
338: sav = isr->sav;
339: if (!tunalready) {
340: union sockaddr_union *dst = &sav->sah->saidx.dst;
341: int setdf;
342:
343: /*
344: * Collect IP_DF state from the outer header.
345: */
346: if (dst->sa.sa_family == AF_INET) {
347: if (m->m_len < sizeof (struct ip) &&
348: (m = m_pullup(m, sizeof (struct ip))) == NULL) {
349: error = ENOBUFS;
350: goto bad;
351: }
352: ip = mtod(m, struct ip *);
353: /* Honor system-wide control of how to handle IP_DF */
354: switch (ip4_ipsec_dfbit) {
355: case 0: /* clear in outer header */
356: case 1: /* set in outer header */
357: setdf = ip4_ipsec_dfbit;
358: break;
359: default: /* propagate to outer header */
1.3 jonathan 360: setdf = ip->ip_off;
361: #ifndef __FreeBSD__
362: /* On FreeBSD, ip_off and ip_len assumed in host endian. */
363: setdf = ntohs(setdf);
364: #endif
365: setdf = htons(setdf & IP_DF);
1.1 jonathan 366: break;
367: }
368: } else {
369: ip = NULL; /* keep compiler happy */
370: setdf = 0;
371: }
372: /* Do the appropriate encapsulation, if necessary */
373: if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
374: dst->sa.sa_family != AF_INET || /* PF mismatch */
375: #if 0
376: (sav->flags & SADB_X_SAFLAGS_TUNNEL) || /* Tunnel requ'd */
377: sav->tdb_xform->xf_type == XF_IP4 || /* ditto */
378: #endif
379: (dst->sa.sa_family == AF_INET && /* Proxy */
380: dst->sin.sin_addr.s_addr != INADDR_ANY &&
381: dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) {
382: struct mbuf *mp;
383:
384: /* Fix IPv4 header checksum and length */
385: if (m->m_len < sizeof (struct ip) &&
386: (m = m_pullup(m, sizeof (struct ip))) == NULL) {
387: error = ENOBUFS;
388: goto bad;
389: }
390: ip = mtod(m, struct ip *);
391: ip->ip_len = htons(m->m_pkthdr.len);
392: ip->ip_sum = 0;
393: #ifdef _IP_VHL
394: if (ip->ip_vhl == IP_VHL_BORING)
395: ip->ip_sum = in_cksum_hdr(ip);
396: else
397: ip->ip_sum = in_cksum(m,
398: _IP_VHL_HL(ip->ip_vhl) << 2);
399: #else
400: ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
401: #endif
402:
403: /* Encapsulate the packet */
404: error = ipip_output(m, isr, &mp, 0, 0);
405: if (mp == NULL && !error) {
406: /* Should never happen. */
407: DPRINTF(("ipsec4_process_packet: ipip_output "
408: "returns no mbuf and no error!"));
409: error = EFAULT;
410: }
411: if (error) {
412: if (mp)
413: m_freem(mp);
414: goto bad;
415: }
416: m = mp, mp = NULL;
417: /*
418: * ipip_output clears IP_DF in the new header. If
419: * we need to propagate IP_DF from the outer header,
420: * then we have to do it here.
421: *
422: * XXX shouldn't assume what ipip_output does.
423: */
424: if (dst->sa.sa_family == AF_INET && setdf) {
425: if (m->m_len < sizeof (struct ip) &&
426: (m = m_pullup(m, sizeof (struct ip))) == NULL) {
427: error = ENOBUFS;
428: goto bad;
429: }
430: ip = mtod(m, struct ip *);
431: ip->ip_off = ntohs(ip->ip_off);
432: ip->ip_off |= IP_DF;
433: ip->ip_off = htons(ip->ip_off);
434: }
435: }
436: }
437:
438: /*
439: * Dispatch to the appropriate IPsec transform logic. The
440: * packet will be returned for transmission after crypto
441: * processing, etc. are completed. For encapsulation we
442: * bypass this call because of the explicit call done above
443: * (necessary to deal with IP_DF handling for IPv4).
444: *
445: * NB: m & sav are ``passed to caller'' who's reponsible for
446: * for reclaiming their resources.
447: */
448: if (sav->tdb_xform->xf_type != XF_IP4) {
449: ip = mtod(m, struct ip *);
450: i = ip->ip_hl << 2;
451: off = offsetof(struct ip, ip_p);
452: error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off);
453: } else {
454: error = ipsec_process_done(m, isr);
455: }
456: splx(s);
457: return error;
458: bad:
459: splx(s);
460: if (m)
461: m_freem(m);
462: return error;
463: }
464: #endif
465:
466: #ifdef INET6
467: /*
468: * Chop IP6 header from the payload.
469: */
470: static struct mbuf *
471: ipsec6_splithdr(struct mbuf *m)
472: {
473: struct mbuf *mh;
474: struct ip6_hdr *ip6;
475: int hlen;
476:
477: IPSEC_ASSERT(m->m_len >= sizeof (struct ip6_hdr),
478: ("ipsec6_splithdr: first mbuf too short, len %u", m->m_len));
479: ip6 = mtod(m, struct ip6_hdr *);
480: hlen = sizeof(struct ip6_hdr);
481: if (m->m_len > hlen) {
482: MGETHDR(mh, M_DONTWAIT, MT_HEADER);
483: if (!mh) {
484: m_freem(m);
485: return NULL;
486: }
487: M_MOVE_PKTHDR(mh, m);
488: MH_ALIGN(mh, hlen);
489: m->m_len -= hlen;
490: m->m_data += hlen;
491: mh->m_next = m;
492: m = mh;
493: m->m_len = hlen;
494: bcopy((caddr_t)ip6, mtod(m, caddr_t), hlen);
495: } else if (m->m_len < hlen) {
496: m = m_pullup(m, hlen);
497: if (!m)
498: return NULL;
499: }
500: return m;
501: }
502:
503: /*
504: * IPsec output logic for IPv6, transport mode.
505: */
506: int
507: ipsec6_output_trans(
508: struct ipsec_output_state *state,
509: u_char *nexthdrp,
510: struct mbuf *mprev,
511: struct secpolicy *sp,
512: int flags,
513: int *tun)
514: {
515: struct ipsecrequest *isr;
516: struct secasindex saidx;
517: int error = 0;
518: struct mbuf *m;
519:
520: IPSEC_ASSERT(state != NULL, ("ipsec6_output: null state"));
521: IPSEC_ASSERT(state->m != NULL, ("ipsec6_output: null m"));
522: IPSEC_ASSERT(nexthdrp != NULL, ("ipsec6_output: null nexthdrp"));
523: IPSEC_ASSERT(mprev != NULL, ("ipsec6_output: null mprev"));
524: IPSEC_ASSERT(sp != NULL, ("ipsec6_output: null sp"));
525: IPSEC_ASSERT(tun != NULL, ("ipsec6_output: null tun"));
526:
527: KEYDEBUG(KEYDEBUG_IPSEC_DATA,
528: printf("ipsec6_output_trans: applyed SP\n");
529: kdebug_secpolicy(sp));
530:
531: isr = sp->req;
532: if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
533: /* the rest will be handled by ipsec6_output_tunnel() */
534: *tun = 1; /* need tunnel-mode processing */
535: return 0;
536: }
537:
538: *tun = 0;
539: m = state->m;
540:
541: isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error);
542: if (isr == NULL) {
543: #ifdef notdef
544: /* XXX should notification be done for all errors ? */
545: /*
546: * Notify the fact that the packet is discarded
547: * to ourselves. I believe this is better than
548: * just silently discarding. (jinmei@kame.net)
549: * XXX: should we restrict the error to TCP packets?
550: * XXX: should we directly notify sockets via
551: * pfctlinputs?
552: */
553: icmp6_error(m, ICMP6_DST_UNREACH,
554: ICMP6_DST_UNREACH_ADMIN, 0);
555: m = NULL; /* NB: icmp6_error frees mbuf */
556: #endif
557: goto bad;
558: }
559:
560: return (*isr->sav->tdb_xform->xf_output)(m, isr, NULL,
561: sizeof (struct ip6_hdr),
562: offsetof(struct ip6_hdr, ip6_nxt));
563: bad:
564: if (m)
565: m_freem(m);
566: state->m = NULL;
567: return error;
568: }
569:
570: static int
571: ipsec6_encapsulate(struct mbuf *m, struct secasvar *sav)
572: {
573: struct ip6_hdr *oip6;
574: struct ip6_hdr *ip6;
575: size_t plen;
576:
577: /* can't tunnel between different AFs */
578: if (sav->sah->saidx.src.sa.sa_family != AF_INET6 ||
579: sav->sah->saidx.dst.sa.sa_family != AF_INET6) {
580: m_freem(m);
581: return EINVAL;
582: }
583: IPSEC_ASSERT(m->m_len != sizeof (struct ip6_hdr),
584: ("ipsec6_encapsulate: mbuf wrong size; len %u", m->m_len));
585:
586:
587: /*
588: * grow the mbuf to accomodate the new IPv6 header.
589: */
590: plen = m->m_pkthdr.len;
591: if (M_LEADINGSPACE(m->m_next) < sizeof(struct ip6_hdr)) {
592: struct mbuf *n;
593: MGET(n, M_DONTWAIT, MT_DATA);
594: if (!n) {
595: m_freem(m);
596: return ENOBUFS;
597: }
598: n->m_len = sizeof(struct ip6_hdr);
599: n->m_next = m->m_next;
600: m->m_next = n;
601: m->m_pkthdr.len += sizeof(struct ip6_hdr);
602: oip6 = mtod(n, struct ip6_hdr *);
603: } else {
604: m->m_next->m_len += sizeof(struct ip6_hdr);
605: m->m_next->m_data -= sizeof(struct ip6_hdr);
606: m->m_pkthdr.len += sizeof(struct ip6_hdr);
607: oip6 = mtod(m->m_next, struct ip6_hdr *);
608: }
609: ip6 = mtod(m, struct ip6_hdr *);
610: ovbcopy((caddr_t)ip6, (caddr_t)oip6, sizeof(struct ip6_hdr));
611:
612: /* Fake link-local scope-class addresses */
613: if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src))
614: oip6->ip6_src.s6_addr16[1] = 0;
615: if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst))
616: oip6->ip6_dst.s6_addr16[1] = 0;
617:
618: /* construct new IPv6 header. see RFC 2401 5.1.2.2 */
619: /* ECN consideration. */
620: ip6_ecn_ingress(ip6_ipsec_ecn, &ip6->ip6_flow, &oip6->ip6_flow);
621: if (plen < IPV6_MAXPACKET - sizeof(struct ip6_hdr))
622: ip6->ip6_plen = htons(plen);
623: else {
624: /* ip6->ip6_plen will be updated in ip6_output() */
625: }
626: ip6->ip6_nxt = IPPROTO_IPV6;
627: sav->sah->saidx.src.sin6.sin6_addr = ip6->ip6_src;
628: sav->sah->saidx.dst.sin6.sin6_addr = ip6->ip6_dst;
629: ip6->ip6_hlim = IPV6_DEFHLIM;
630:
631: /* XXX Should ip6_src be updated later ? */
632:
633: return 0;
634: }
635:
636: /*
637: * IPsec output logic for IPv6, tunnel mode.
638: */
639: int
640: ipsec6_output_tunnel(struct ipsec_output_state *state, struct secpolicy *sp, int flags)
641: {
642: struct ip6_hdr *ip6;
643: struct ipsecrequest *isr;
644: struct secasindex saidx;
645: int error;
646: struct sockaddr_in6* dst6;
647: struct mbuf *m;
648:
649: IPSEC_ASSERT(state != NULL, ("ipsec6_output: null state"));
650: IPSEC_ASSERT(state->m != NULL, ("ipsec6_output: null m"));
651: IPSEC_ASSERT(sp != NULL, ("ipsec6_output: null sp"));
652:
653: KEYDEBUG(KEYDEBUG_IPSEC_DATA,
654: printf("ipsec6_output_tunnel: applyed SP\n");
655: kdebug_secpolicy(sp));
656:
657: m = state->m;
658: /*
659: * transport mode ipsec (before the 1st tunnel mode) is already
660: * processed by ipsec6_output_trans().
661: */
662: for (isr = sp->req; isr; isr = isr->next) {
663: if (isr->saidx.mode == IPSEC_MODE_TUNNEL)
664: break;
665: }
666: isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error);
667: if (isr == NULL)
668: goto bad;
669:
670: /*
671: * There may be the case that SA status will be changed when
672: * we are refering to one. So calling splsoftnet().
673: */
674: if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
675: /*
676: * build IPsec tunnel.
677: */
678: /* XXX should be processed with other familiy */
679: if (isr->sav->sah->saidx.src.sa.sa_family != AF_INET6) {
680: ipseclog((LOG_ERR, "ipsec6_output_tunnel: "
681: "family mismatched between inner and outer, spi=%lu\n",
682: ntohl(isr->sav->spi)));
683: newipsecstat.ips_out_inval++;
684: error = EAFNOSUPPORT;
685: goto bad;
686: }
687:
688: m = ipsec6_splithdr(m);
689: if (!m) {
690: newipsecstat.ips_out_nomem++;
691: error = ENOMEM;
692: goto bad;
693: }
694: error = ipsec6_encapsulate(m, isr->sav);
695: if (error) {
696: m = NULL;
697: goto bad;
698: }
699: ip6 = mtod(m, struct ip6_hdr *);
700:
701: state->ro = &isr->sav->sah->sa_route;
702: state->dst = (struct sockaddr *)&state->ro->ro_dst;
703: dst6 = (struct sockaddr_in6 *)state->dst;
704: if (state->ro->ro_rt
705: && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0
706: || !IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ip6->ip6_dst))) {
707: RTFREE(state->ro->ro_rt);
708: state->ro->ro_rt = NULL;
709: }
710: if (state->ro->ro_rt == 0) {
711: bzero(dst6, sizeof(*dst6));
712: dst6->sin6_family = AF_INET6;
713: dst6->sin6_len = sizeof(*dst6);
714: dst6->sin6_addr = ip6->ip6_dst;
715: rtalloc(state->ro);
716: }
717: if (state->ro->ro_rt == 0) {
718: ip6stat.ip6s_noroute++;
719: newipsecstat.ips_out_noroute++;
720: error = EHOSTUNREACH;
721: goto bad;
722: }
723:
724: /* adjust state->dst if tunnel endpoint is offlink */
725: if (state->ro->ro_rt->rt_flags & RTF_GATEWAY) {
726: state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway;
727: dst6 = (struct sockaddr_in6 *)state->dst;
728: }
729: }
730:
731: m = ipsec6_splithdr(m);
732: if (!m) {
733: newipsecstat.ips_out_nomem++;
734: error = ENOMEM;
735: goto bad;
736: }
737: ip6 = mtod(m, struct ip6_hdr *);
738: return (*isr->sav->tdb_xform->xf_output)(m, isr, NULL,
739: sizeof (struct ip6_hdr),
740: offsetof(struct ip6_hdr, ip6_nxt));
741: bad:
742: if (m)
743: m_freem(m);
744: state->m = NULL;
745: return error;
746: }
747: #endif /*INET6*/
CVSweb <webmaster@jp.NetBSD.org>