1 | /* $NetBSD: ipsec_output.c,v 1.41 2015/03/30 03:51:50 ozaki-r Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
26 | * SUCH DAMAGE. |
27 | * |
28 | * $FreeBSD: /repoman/r/ncvs/src/sys/netipsec/ipsec_output.c,v 1.3.2.2 2003/03/28 20:32:53 sam Exp $ |
29 | */ |
30 | |
31 | #include <sys/cdefs.h> |
32 | __KERNEL_RCSID(0, "$NetBSD: ipsec_output.c,v 1.41 2015/03/30 03:51:50 ozaki-r Exp $" ); |
33 | |
34 | /* |
35 | * IPsec output processing. |
36 | */ |
37 | #include "opt_inet.h" |
38 | #ifdef __FreeBSD__ |
39 | #include "opt_inet6.h" |
40 | #endif |
41 | |
42 | #include <sys/param.h> |
43 | #include <sys/systm.h> |
44 | #include <sys/mbuf.h> |
45 | #include <sys/domain.h> |
46 | #include <sys/protosw.h> |
47 | #include <sys/socket.h> |
48 | #include <sys/errno.h> |
49 | #include <sys/syslog.h> |
50 | |
51 | #include <net/if.h> |
52 | #include <net/route.h> |
53 | |
54 | #include <netinet/in.h> |
55 | #include <netinet/in_systm.h> |
56 | #include <netinet/ip.h> |
57 | #include <netinet/ip_var.h> |
58 | #include <netinet/in_var.h> |
59 | #include <netinet/ip_ecn.h> |
60 | #ifdef INET6 |
61 | # ifdef __FreeBSD__ |
62 | # include <netinet6/ip6_ecn.h> |
63 | # endif |
64 | #endif |
65 | |
66 | #include <netinet/ip6.h> |
67 | #ifdef INET6 |
68 | #include <netinet6/ip6_var.h> |
69 | #endif |
70 | #include <netinet/in_pcb.h> |
71 | #ifdef INET6 |
72 | #include <netinet/icmp6.h> |
73 | #endif |
74 | #include <netinet/udp.h> |
75 | |
76 | #include <netipsec/ipsec.h> |
77 | #include <netipsec/ipsec_var.h> |
78 | #include <netipsec/ipsec_private.h> |
79 | #ifdef INET6 |
80 | #include <netipsec/ipsec6.h> |
81 | #endif |
82 | #include <netipsec/ah_var.h> |
83 | #include <netipsec/esp_var.h> |
84 | #include <netipsec/ipcomp_var.h> |
85 | |
86 | #include <netipsec/xform.h> |
87 | |
88 | #include <netipsec/key.h> |
89 | #include <netipsec/keydb.h> |
90 | #include <netipsec/key_debug.h> |
91 | #include <netipsec/ipsec_osdep.h> |
92 | |
93 | #include <net/net_osdep.h> /* ovbcopy() in ipsec6_encapsulate() */ |
94 | |
95 | |
96 | /* |
97 | * Add a IPSEC_OUT_DONE tag to mark that we have finished the ipsec processing |
98 | * It will be used by ip{,6}_output to check if we have already or not |
99 | * processed this packet. |
100 | */ |
101 | static int |
102 | ipsec_register_done(struct mbuf *m, int * error) |
103 | { |
104 | struct m_tag *mtag; |
105 | |
106 | mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, 0, M_NOWAIT); |
107 | if (mtag == NULL) { |
108 | DPRINTF(("ipsec_register_done: could not get packet tag\n" )); |
109 | *error = ENOMEM; |
110 | return -1; |
111 | } |
112 | |
113 | m_tag_prepend(m, mtag); |
114 | return 0; |
115 | } |
116 | |
117 | static int |
118 | ipsec_reinject_ipstack(struct mbuf *m, int af) |
119 | { |
120 | #ifdef INET |
121 | #ifdef __FreeBSD__ |
122 | struct ip *ip; |
123 | #endif /* __FreeBSD_ */ |
124 | #endif /* INET */ |
125 | #if defined(INET) || defined(INET6) |
126 | int rv; |
127 | #endif |
128 | |
129 | switch (af) { |
130 | #ifdef INET |
131 | case AF_INET: |
132 | #ifdef __FreeBSD__ |
133 | ip = mtod(m, struct ip *); |
134 | /* FreeBSD ip_output() expects ip_len, ip_off in host endian */ |
135 | ip->ip_len = ntohs(ip->ip_len); |
136 | ip->ip_off = ntohs(ip->ip_off); |
137 | #endif /* __FreeBSD_ */ |
138 | KERNEL_LOCK(1, NULL); |
139 | rv = ip_output(m, NULL, NULL, IP_RAWOUTPUT|IP_NOIPNEWID, |
140 | NULL, NULL); |
141 | KERNEL_UNLOCK_ONE(NULL); |
142 | return rv; |
143 | |
144 | #endif /* INET */ |
145 | #ifdef INET6 |
146 | case AF_INET6: |
147 | /* |
148 | * We don't need massage, IPv6 header fields are always in |
149 | * net endian. |
150 | */ |
151 | KERNEL_LOCK(1, NULL); |
152 | rv = ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); |
153 | KERNEL_UNLOCK_ONE(NULL); |
154 | return rv; |
155 | #endif /* INET6 */ |
156 | } |
157 | |
158 | panic("ipsec_reinject_ipstack : iunknown protocol family %u\n" , af); |
159 | return -1; /* NOTREACHED */ |
160 | } |
161 | |
162 | int |
163 | ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr) |
164 | { |
165 | struct secasvar *sav; |
166 | struct secasindex *saidx; |
167 | int error; |
168 | #ifdef INET |
169 | struct ip * ip; |
170 | #endif /* INET */ |
171 | #ifdef INET6 |
172 | struct ip6_hdr * ip6; |
173 | #endif /* INET6 */ |
174 | struct mbuf * mo; |
175 | struct udphdr *udp = NULL; |
176 | uint64_t * data = NULL; |
177 | int hlen, roff; |
178 | |
179 | IPSEC_SPLASSERT_SOFTNET("ipsec_process_done" ); |
180 | |
181 | IPSEC_ASSERT(m != NULL, ("ipsec_process_done: null mbuf" )); |
182 | IPSEC_ASSERT(isr != NULL, ("ipsec_process_done: null ISR" )); |
183 | sav = isr->sav; |
184 | IPSEC_ASSERT(sav != NULL, ("ipsec_process_done: null SA" )); |
185 | IPSEC_ASSERT(sav->sah != NULL, ("ipsec_process_done: null SAH" )); |
186 | |
187 | saidx = &sav->sah->saidx; |
188 | |
189 | if(sav->natt_type != 0) { |
190 | ip = mtod(m, struct ip *); |
191 | |
192 | hlen = sizeof(struct udphdr); |
193 | if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) |
194 | hlen += sizeof(uint64_t); |
195 | |
196 | mo = m_makespace(m, sizeof(struct ip), hlen, &roff); |
197 | if (mo == NULL) { |
198 | DPRINTF(("ipsec_process_done : failed to inject" |
199 | "%u byte UDP for SA %s/%08lx\n" , |
200 | hlen, ipsec_address(&saidx->dst), |
201 | (u_long) ntohl(sav->spi))); |
202 | error = ENOBUFS; |
203 | goto bad; |
204 | } |
205 | |
206 | udp = (struct udphdr*) (mtod(mo, char*) + roff); |
207 | data = (uint64_t*) (udp + 1); |
208 | |
209 | if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) |
210 | *data = 0; /* NON-IKE Marker */ |
211 | |
212 | if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) |
213 | udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT); |
214 | else |
215 | udp->uh_sport = key_portfromsaddr(&saidx->src); |
216 | |
217 | udp->uh_dport = key_portfromsaddr(&saidx->dst); |
218 | udp->uh_sum = 0; |
219 | udp->uh_ulen = htons(m->m_pkthdr.len - (ip->ip_hl << 2)); |
220 | } |
221 | |
222 | switch (saidx->dst.sa.sa_family) { |
223 | #ifdef INET |
224 | case AF_INET: |
225 | /* Fix the header length, for AH processing. */ |
226 | ip = mtod(m, struct ip *); |
227 | ip->ip_len = htons(m->m_pkthdr.len); |
228 | if (sav->natt_type != 0) |
229 | ip->ip_p = IPPROTO_UDP; |
230 | break; |
231 | #endif /* INET */ |
232 | #ifdef INET6 |
233 | case AF_INET6: |
234 | /* Fix the header length, for AH processing. */ |
235 | if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) { |
236 | error = ENXIO; |
237 | goto bad; |
238 | } |
239 | if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) { |
240 | /* No jumbogram support. */ |
241 | error = ENXIO; /*?*/ |
242 | goto bad; |
243 | } |
244 | ip6 = mtod(m, struct ip6_hdr *); |
245 | ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); |
246 | if (sav->natt_type != 0) |
247 | ip6->ip6_nxt = IPPROTO_UDP; |
248 | break; |
249 | #endif /* INET6 */ |
250 | default: |
251 | DPRINTF(("ipsec_process_done: unknown protocol family %u\n" , |
252 | saidx->dst.sa.sa_family)); |
253 | error = ENXIO; |
254 | goto bad; |
255 | } |
256 | |
257 | key_sa_recordxfer(sav, m); |
258 | |
259 | /* |
260 | * If there's another (bundled) SA to apply, do so. |
261 | * Note that this puts a burden on the kernel stack size. |
262 | * If this is a problem we'll need to introduce a queue |
263 | * to set the packet on so we can unwind the stack before |
264 | * doing further processing. |
265 | */ |
266 | if (isr->next) { |
267 | IPSEC_STATINC(IPSEC_STAT_OUT_BUNDLESA); |
268 | switch ( saidx->dst.sa.sa_family ) { |
269 | #ifdef INET |
270 | case AF_INET: |
271 | return ipsec4_process_packet(m, isr->next, 0,0); |
272 | #endif /* INET */ |
273 | #ifdef INET6 |
274 | case AF_INET6: |
275 | return ipsec6_process_packet(m,isr->next); |
276 | #endif /* INET6 */ |
277 | default : |
278 | DPRINTF(("ipsec_process_done: unknown protocol family %u\n" , |
279 | saidx->dst.sa.sa_family)); |
280 | error = ENXIO; |
281 | goto bad; |
282 | } |
283 | } |
284 | |
285 | /* |
286 | * We're done with IPsec processing, |
287 | * mark that we have already processed the packet |
288 | * transmit it packet using the appropriate network protocol (IP or IPv6). |
289 | */ |
290 | |
291 | if (ipsec_register_done(m, &error) < 0) |
292 | goto bad; |
293 | |
294 | return ipsec_reinject_ipstack(m, saidx->dst.sa.sa_family); |
295 | bad: |
296 | m_freem(m); |
297 | KEY_FREESAV(&sav); |
298 | return (error); |
299 | } |
300 | |
301 | /* |
302 | * ipsec_nextisr can return : |
303 | * - isr == NULL and error != 0 => something is bad : the packet must be |
304 | * discarded |
305 | * - isr == NULL and error == 0 => no more rules to apply, ipsec processing |
306 | * is done, reinject it in ip stack |
307 | * - isr != NULL (error == 0) => we need to apply one rule to the packet |
308 | */ |
309 | static struct ipsecrequest * |
310 | ipsec_nextisr( |
311 | struct mbuf *m, |
312 | struct ipsecrequest *isr, |
313 | int af, |
314 | struct secasindex *saidx, |
315 | int *error |
316 | ) |
317 | { |
318 | #define IPSEC_OSTAT(x, y, z) \ |
319 | do { \ |
320 | switch (isr->saidx.proto) { \ |
321 | case IPPROTO_ESP: \ |
322 | ESP_STATINC(x); \ |
323 | break; \ |
324 | case IPPROTO_AH: \ |
325 | AH_STATINC(y); \ |
326 | break; \ |
327 | default: \ |
328 | IPCOMP_STATINC(z); \ |
329 | break; \ |
330 | } \ |
331 | } while (/*CONSTCOND*/0) |
332 | |
333 | struct secasvar *sav; |
334 | |
335 | IPSEC_SPLASSERT_SOFTNET("ipsec_nextisr" ); |
336 | IPSEC_ASSERT(af == AF_INET || af == AF_INET6, |
337 | ("ipsec_nextisr: invalid address family %u" , af)); |
338 | again: |
339 | /* |
340 | * Craft SA index to search for proper SA. Note that |
341 | * we only fillin unspecified SA peers for transport |
342 | * mode; for tunnel mode they must already be filled in. |
343 | */ |
344 | *saidx = isr->saidx; |
345 | if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) { |
346 | /* Fillin unspecified SA peers only for transport mode */ |
347 | if (af == AF_INET) { |
348 | struct sockaddr_in *sin; |
349 | struct ip *ip = mtod(m, struct ip *); |
350 | |
351 | if (saidx->src.sa.sa_len == 0) { |
352 | sin = &saidx->src.sin; |
353 | sin->sin_len = sizeof(*sin); |
354 | sin->sin_family = AF_INET; |
355 | sin->sin_port = IPSEC_PORT_ANY; |
356 | sin->sin_addr = ip->ip_src; |
357 | } |
358 | if (saidx->dst.sa.sa_len == 0) { |
359 | sin = &saidx->dst.sin; |
360 | sin->sin_len = sizeof(*sin); |
361 | sin->sin_family = AF_INET; |
362 | sin->sin_port = IPSEC_PORT_ANY; |
363 | sin->sin_addr = ip->ip_dst; |
364 | } |
365 | } else { |
366 | struct sockaddr_in6 *sin6; |
367 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
368 | |
369 | if (saidx->src.sin6.sin6_len == 0) { |
370 | sin6 = (struct sockaddr_in6 *)&saidx->src; |
371 | sin6->sin6_len = sizeof(*sin6); |
372 | sin6->sin6_family = AF_INET6; |
373 | sin6->sin6_port = IPSEC_PORT_ANY; |
374 | sin6->sin6_addr = ip6->ip6_src; |
375 | if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { |
376 | /* fix scope id for comparing SPD */ |
377 | sin6->sin6_addr.s6_addr16[1] = 0; |
378 | sin6->sin6_scope_id = |
379 | ntohs(ip6->ip6_src.s6_addr16[1]); |
380 | } |
381 | } |
382 | if (saidx->dst.sin6.sin6_len == 0) { |
383 | sin6 = (struct sockaddr_in6 *)&saidx->dst; |
384 | sin6->sin6_len = sizeof(*sin6); |
385 | sin6->sin6_family = AF_INET6; |
386 | sin6->sin6_port = IPSEC_PORT_ANY; |
387 | sin6->sin6_addr = ip6->ip6_dst; |
388 | if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { |
389 | /* fix scope id for comparing SPD */ |
390 | sin6->sin6_addr.s6_addr16[1] = 0; |
391 | sin6->sin6_scope_id = |
392 | ntohs(ip6->ip6_dst.s6_addr16[1]); |
393 | } |
394 | } |
395 | } |
396 | } |
397 | |
398 | /* |
399 | * Lookup SA and validate it. |
400 | */ |
401 | *error = key_checkrequest(isr, saidx); |
402 | if (*error != 0) { |
403 | /* |
404 | * IPsec processing is required, but no SA found. |
405 | * I assume that key_acquire() had been called |
406 | * to get/establish the SA. Here I discard |
407 | * this packet because it is responsibility for |
408 | * upper layer to retransmit the packet. |
409 | */ |
410 | IPSEC_STATINC(IPSEC_STAT_OUT_NOSA); |
411 | goto bad; |
412 | } |
413 | sav = isr->sav; |
414 | /* sav may be NULL here if we have an USE rule */ |
415 | if (sav == NULL) { |
416 | IPSEC_ASSERT(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE, |
417 | ("ipsec_nextisr: no SA found, but required; level %u" , |
418 | ipsec_get_reqlevel(isr))); |
419 | isr = isr->next; |
420 | /* |
421 | * No more rules to apply, return NULL isr and no error |
422 | * It can happen when the last rules are USE rules |
423 | * */ |
424 | if (isr == NULL) { |
425 | *error = 0; |
426 | return isr; |
427 | } |
428 | goto again; |
429 | } |
430 | |
431 | /* |
432 | * Check system global policy controls. |
433 | */ |
434 | if ((isr->saidx.proto == IPPROTO_ESP && !esp_enable) || |
435 | (isr->saidx.proto == IPPROTO_AH && !ah_enable) || |
436 | (isr->saidx.proto == IPPROTO_IPCOMP && !ipcomp_enable)) { |
437 | DPRINTF(("ipsec_nextisr: IPsec outbound packet dropped due" |
438 | " to policy (check your sysctls)\n" )); |
439 | IPSEC_OSTAT(ESP_STAT_PDROPS, AH_STAT_PDROPS, |
440 | IPCOMP_STAT_PDROPS); |
441 | *error = EHOSTUNREACH; |
442 | goto bad; |
443 | } |
444 | |
445 | /* |
446 | * Sanity check the SA contents for the caller |
447 | * before they invoke the xform output method. |
448 | */ |
449 | if (sav->tdb_xform == NULL) { |
450 | DPRINTF(("ipsec_nextisr: no transform for SA\n" )); |
451 | IPSEC_OSTAT(ESP_STAT_NOXFORM, AH_STAT_NOXFORM, |
452 | IPCOMP_STAT_NOXFORM); |
453 | *error = EHOSTUNREACH; |
454 | goto bad; |
455 | } |
456 | return isr; |
457 | bad: |
458 | IPSEC_ASSERT(*error != 0, ("ipsec_nextisr: error return w/ no error code" )); |
459 | return NULL; |
460 | #undef IPSEC_OSTAT |
461 | } |
462 | |
463 | #ifdef INET |
464 | /* |
465 | * IPsec output logic for IPv4. |
466 | */ |
467 | int |
468 | ipsec4_process_packet( |
469 | struct mbuf *m, |
470 | struct ipsecrequest *isr, |
471 | int flags, |
472 | int tunalready |
473 | ) |
474 | { |
475 | struct secasindex saidx; |
476 | struct secasvar *sav; |
477 | struct ip *ip; |
478 | int s, error, i, off; |
479 | |
480 | IPSEC_ASSERT(m != NULL, ("ipsec4_process_packet: null mbuf" )); |
481 | IPSEC_ASSERT(isr != NULL, ("ipsec4_process_packet: null isr" )); |
482 | |
483 | s = splsoftnet(); /* insure SA contents don't change */ |
484 | |
485 | isr = ipsec_nextisr(m, isr, AF_INET, &saidx, &error); |
486 | if (isr == NULL) { |
487 | if (error != 0) { |
488 | goto bad; |
489 | } else { |
490 | if (ipsec_register_done(m, &error) < 0) |
491 | goto bad; |
492 | |
493 | splx(s); |
494 | return ipsec_reinject_ipstack(m, AF_INET); |
495 | } |
496 | } |
497 | |
498 | sav = isr->sav; |
499 | if (!tunalready) { |
500 | union sockaddr_union *dst = &sav->sah->saidx.dst; |
501 | int setdf; |
502 | |
503 | /* |
504 | * Collect IP_DF state from the outer header. |
505 | */ |
506 | if (dst->sa.sa_family == AF_INET) { |
507 | if (m->m_len < sizeof (struct ip) && |
508 | (m = m_pullup(m, sizeof (struct ip))) == NULL) { |
509 | error = ENOBUFS; |
510 | goto bad; |
511 | } |
512 | ip = mtod(m, struct ip *); |
513 | /* Honor system-wide control of how to handle IP_DF */ |
514 | switch (ip4_ipsec_dfbit) { |
515 | case 0: /* clear in outer header */ |
516 | case 1: /* set in outer header */ |
517 | setdf = ip4_ipsec_dfbit; |
518 | break; |
519 | default: /* propagate to outer header */ |
520 | setdf = ip->ip_off; |
521 | #ifndef __FreeBSD__ |
522 | /* On FreeBSD, ip_off and ip_len assumed in host endian. */ |
523 | setdf = ntohs(setdf); |
524 | #endif |
525 | setdf = htons(setdf & IP_DF); |
526 | break; |
527 | } |
528 | } else { |
529 | ip = NULL; /* keep compiler happy */ |
530 | setdf = 0; |
531 | } |
532 | /* Do the appropriate encapsulation, if necessary */ |
533 | if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ |
534 | dst->sa.sa_family != AF_INET || /* PF mismatch */ |
535 | #if 0 |
536 | (sav->flags & SADB_X_SAFLAGS_TUNNEL) || /* Tunnel requ'd */ |
537 | sav->tdb_xform->xf_type == XF_IP4 || /* ditto */ |
538 | #endif |
539 | (dst->sa.sa_family == AF_INET && /* Proxy */ |
540 | dst->sin.sin_addr.s_addr != INADDR_ANY && |
541 | dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) { |
542 | struct mbuf *mp; |
543 | |
544 | /* Fix IPv4 header checksum and length */ |
545 | if (m->m_len < sizeof (struct ip) && |
546 | (m = m_pullup(m, sizeof (struct ip))) == NULL) { |
547 | error = ENOBUFS; |
548 | goto bad; |
549 | } |
550 | ip = mtod(m, struct ip *); |
551 | ip->ip_len = htons(m->m_pkthdr.len); |
552 | ip->ip_sum = 0; |
553 | ip->ip_sum = in_cksum(m, ip->ip_hl << 2); |
554 | |
555 | /* Encapsulate the packet */ |
556 | error = ipip_output(m, isr, &mp, 0, 0); |
557 | if (mp == NULL && !error) { |
558 | /* Should never happen. */ |
559 | DPRINTF(("ipsec4_process_packet: ipip_output " |
560 | "returns no mbuf and no error!" )); |
561 | error = EFAULT; |
562 | } |
563 | if (error) { |
564 | if (mp) { |
565 | /* XXX: Should never happen! */ |
566 | m_freem(mp); |
567 | } |
568 | m = NULL; /* ipip_output() already freed it */ |
569 | goto bad; |
570 | } |
571 | m = mp, mp = NULL; |
572 | /* |
573 | * ipip_output clears IP_DF in the new header. If |
574 | * we need to propagate IP_DF from the outer header, |
575 | * then we have to do it here. |
576 | * |
577 | * XXX shouldn't assume what ipip_output does. |
578 | */ |
579 | if (dst->sa.sa_family == AF_INET && setdf) { |
580 | if (m->m_len < sizeof (struct ip) && |
581 | (m = m_pullup(m, sizeof (struct ip))) == NULL) { |
582 | error = ENOBUFS; |
583 | goto bad; |
584 | } |
585 | ip = mtod(m, struct ip *); |
586 | ip->ip_off |= IP_OFF_CONVERT(IP_DF); |
587 | } |
588 | } |
589 | } |
590 | |
591 | /* |
592 | * Dispatch to the appropriate IPsec transform logic. The |
593 | * packet will be returned for transmission after crypto |
594 | * processing, etc. are completed. For encapsulation we |
595 | * bypass this call because of the explicit call done above |
596 | * (necessary to deal with IP_DF handling for IPv4). |
597 | * |
598 | * NB: m & sav are ``passed to caller'' who's reponsible for |
599 | * for reclaiming their resources. |
600 | */ |
601 | if (sav->tdb_xform->xf_type != XF_IP4) { |
602 | union sockaddr_union *dst = &sav->sah->saidx.dst; |
603 | if (dst->sa.sa_family == AF_INET) { |
604 | ip = mtod(m, struct ip *); |
605 | i = ip->ip_hl << 2; |
606 | off = offsetof(struct ip, ip_p); |
607 | } else { |
608 | i = sizeof(struct ip6_hdr); |
609 | off = offsetof(struct ip6_hdr, ip6_nxt); |
610 | } |
611 | error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off); |
612 | } else { |
613 | error = ipsec_process_done(m, isr); |
614 | } |
615 | splx(s); |
616 | return error; |
617 | bad: |
618 | splx(s); |
619 | if (m) |
620 | m_freem(m); |
621 | return error; |
622 | } |
623 | #endif |
624 | |
625 | #ifdef INET6 |
626 | static void |
627 | compute_ipsec_pos(struct mbuf *m, int *i, int *off) |
628 | { |
629 | int nxt; |
630 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr*); |
631 | struct ip6_ext ip6e; |
632 | int dstopt = 0; |
633 | |
634 | *i = sizeof(struct ip6_hdr); |
635 | *off = offsetof(struct ip6_hdr, ip6_nxt); |
636 | nxt = ip6->ip6_nxt; |
637 | |
638 | /* |
639 | * chase mbuf chain to find the appropriate place to |
640 | * put AH/ESP/IPcomp header. |
641 | * IPv6 hbh dest1 rthdr ah* [esp* dest2 payload] |
642 | */ |
643 | do { |
644 | switch (nxt) { |
645 | case IPPROTO_AH: |
646 | case IPPROTO_ESP: |
647 | case IPPROTO_IPCOMP: |
648 | /* |
649 | * we should not skip security header added |
650 | * beforehand. |
651 | */ |
652 | return; |
653 | |
654 | case IPPROTO_HOPOPTS: |
655 | case IPPROTO_DSTOPTS: |
656 | case IPPROTO_ROUTING: |
657 | /* |
658 | * if we see 2nd destination option header, |
659 | * we should stop there. |
660 | */ |
661 | if (nxt == IPPROTO_DSTOPTS && dstopt) |
662 | return; |
663 | |
664 | if (nxt == IPPROTO_DSTOPTS) { |
665 | /* |
666 | * seen 1st or 2nd destination option. |
667 | * next time we see one, it must be 2nd. |
668 | */ |
669 | dstopt = 1; |
670 | } else if (nxt == IPPROTO_ROUTING) { |
671 | /* |
672 | * if we see destionation option next |
673 | * time, it must be dest2. |
674 | */ |
675 | dstopt = 2; |
676 | } |
677 | |
678 | /* skip this header */ |
679 | m_copydata(m, *i, sizeof(ip6e), &ip6e); |
680 | nxt = ip6e.ip6e_nxt; |
681 | *off = *i + offsetof(struct ip6_ext, ip6e_nxt); |
682 | /* |
683 | * we will never see nxt == IPPROTO_AH |
684 | * so it is safe to omit AH case. |
685 | */ |
686 | *i += (ip6e.ip6e_len + 1) << 3; |
687 | break; |
688 | default: |
689 | return; |
690 | } |
691 | } while (*i < m->m_pkthdr.len); |
692 | } |
693 | |
694 | static int |
695 | in6_sa_equal_addrwithscope(const struct sockaddr_in6 *sa, const struct in6_addr *ia) |
696 | { |
697 | struct in6_addr ia2; |
698 | |
699 | memcpy(&ia2, &sa->sin6_addr, sizeof(ia2)); |
700 | if (IN6_IS_SCOPE_LINKLOCAL(&sa->sin6_addr)) |
701 | ia2.s6_addr16[1] = htons(sa->sin6_scope_id); |
702 | |
703 | return IN6_ARE_ADDR_EQUAL(ia, &ia2); |
704 | } |
705 | |
706 | int |
707 | ipsec6_process_packet( |
708 | struct mbuf *m, |
709 | struct ipsecrequest *isr |
710 | ) |
711 | { |
712 | struct secasindex saidx; |
713 | struct secasvar *sav; |
714 | struct ip6_hdr *ip6; |
715 | int s, error, i, off; |
716 | union sockaddr_union *dst; |
717 | |
718 | IPSEC_ASSERT(m != NULL, ("ipsec6_process_packet: null mbuf" )); |
719 | IPSEC_ASSERT(isr != NULL, ("ipsec6_process_packet: null isr" )); |
720 | |
721 | s = splsoftnet(); /* insure SA contents don't change */ |
722 | |
723 | isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error); |
724 | if (isr == NULL) { |
725 | if (error != 0) { |
726 | /* XXX Should we send a notification ? */ |
727 | goto bad; |
728 | } else { |
729 | if (ipsec_register_done(m, &error) < 0) |
730 | goto bad; |
731 | |
732 | splx(s); |
733 | return ipsec_reinject_ipstack(m, AF_INET6); |
734 | } |
735 | } |
736 | |
737 | sav = isr->sav; |
738 | dst = &sav->sah->saidx.dst; |
739 | |
740 | ip6 = mtod(m, struct ip6_hdr *); /* XXX */ |
741 | |
742 | /* Do the appropriate encapsulation, if necessary */ |
743 | if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ |
744 | dst->sa.sa_family != AF_INET6 || /* PF mismatch */ |
745 | ((dst->sa.sa_family == AF_INET6) && |
746 | (!IN6_IS_ADDR_UNSPECIFIED(&dst->sin6.sin6_addr)) && |
747 | (!in6_sa_equal_addrwithscope(&dst->sin6, |
748 | &ip6->ip6_dst)))) { |
749 | struct mbuf *mp; |
750 | |
751 | /* Fix IPv6 header payload length. */ |
752 | if (m->m_len < sizeof(struct ip6_hdr)) |
753 | if ((m = m_pullup(m,sizeof(struct ip6_hdr))) == NULL) |
754 | return ENOBUFS; |
755 | |
756 | if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) { |
757 | /* No jumbogram support. */ |
758 | m_freem(m); |
759 | return ENXIO; /*XXX*/ |
760 | } |
761 | |
762 | ip6 = mtod(m, struct ip6_hdr *); |
763 | ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6)); |
764 | |
765 | /* Encapsulate the packet */ |
766 | error = ipip_output(m, isr, &mp, 0, 0); |
767 | if (mp == NULL && !error) { |
768 | /* Should never happen. */ |
769 | DPRINTF(("ipsec6_process_packet: ipip_output " |
770 | "returns no mbuf and no error!" )); |
771 | error = EFAULT; |
772 | } |
773 | |
774 | if (error) { |
775 | if (mp) { |
776 | /* XXX: Should never happen! */ |
777 | m_freem(mp); |
778 | } |
779 | m = NULL; /* ipip_output() already freed it */ |
780 | goto bad; |
781 | } |
782 | |
783 | m = mp; |
784 | mp = NULL; |
785 | } |
786 | |
787 | if (dst->sa.sa_family == AF_INET) { |
788 | struct ip *ip; |
789 | ip = mtod(m, struct ip *); |
790 | i = ip->ip_hl << 2; |
791 | off = offsetof(struct ip, ip_p); |
792 | } else { |
793 | compute_ipsec_pos(m, &i, &off); |
794 | } |
795 | error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off); |
796 | splx(s); |
797 | return error; |
798 | bad: |
799 | splx(s); |
800 | if (m) |
801 | m_freem(m); |
802 | return error; |
803 | } |
804 | #endif /*INET6*/ |
805 | |