1 /*
2 * Copyright (c) 2018-2025 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /*
30 * Copyright (c) 2001 Daniel Hartmeier
31 * Copyright (c) 2002 - 2013 Henning Brauer
32 * NAT64 - Copyright (c) 2010 Viagenie Inc. (http://www.viagenie.ca)
33 * All rights reserved.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 *
39 * - Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * - Redistributions in binary form must reproduce the above
42 * copyright notice, this list of conditions and the following
43 * disclaimer in the documentation and/or other materials provided
44 * with the distribution.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
47 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
48 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
49 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
50 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
51 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
52 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
53 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
54 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
56 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
57 * POSSIBILITY OF SUCH DAMAGE.
58 *
59 * Effort sponsored in part by the Defense Advanced Research Projects
60 * Agency (DARPA) and Air Force Research Laboratory, Air Force
61 * Materiel Command, USAF, under agreement number F30602-01-2-0537.
62 *
63 */
64 #include <sys/param.h>
65 #include <sys/types.h>
66 #include <sys/mbuf.h>
67
68 #include <net/if.h>
69 #include <net/if_types.h>
70 #include <net/dlil.h>
71 #include <net/nat464_utils.h>
72 #include <net/nwk_wq.h>
73
74 #include <netinet/in.h>
75 #include <netinet/in_var.h>
76 #include <netinet/in_systm.h>
77 #include <netinet/ip.h>
78 #include <netinet/ip6.h>
79 #include <netinet/ip_var.h>
80 #include <netinet/ip_icmp.h>
81 #include <netinet/in_pcb.h>
82 #include <netinet/icmp_var.h>
83 #include <netinet/icmp6.h>
84 #include <netinet/tcp.h>
85 #include <netinet/udp.h>
86 #include <netinet/udp_var.h>
87 #include <os/log.h>
88
89 int clat_debug = 0;
90
91 os_log_t nat_log_handle;
92
93 static void
94 nat464_addr_cksum_fixup(uint16_t *, struct nat464_addr *, struct nat464_addr *,
95 protocol_family_t, protocol_family_t, uint8_t, boolean_t);
96
97 /* Synthesize ipv6 from ipv4 */
98 int
nat464_synthesize_ipv6(ifnet_t ifp,const struct in_addr * addrv4,struct in6_addr * addr)99 nat464_synthesize_ipv6(ifnet_t ifp, const struct in_addr *addrv4, struct in6_addr *addr)
100 {
101 static const struct in6_addr well_known_prefix = {
102 .__u6_addr.__u6_addr8 = {0x00, 0x64, 0xff, 0x9b, 0x00, 0x00,
103 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104 0x00, 0x00, 0x00, 0x00},
105 };
106
107 struct ipv6_prefix nat64prefixes[NAT64_MAX_NUM_PREFIXES];
108 int error = 0, i = 0;
109 /* Below call is not optimized as it creates a copy of prefixes */
110 if ((error = ifnet_get_nat64prefix(ifp, nat64prefixes)) != 0) {
111 return error;
112 }
113
114 for (i = 0; i < NAT64_MAX_NUM_PREFIXES; i++) {
115 if (nat64prefixes[i].prefix_len != 0) {
116 break;
117 }
118 }
119
120 VERIFY(i < NAT64_MAX_NUM_PREFIXES);
121
122 struct in6_addr prefix = nat64prefixes[i].ipv6_prefix;
123 int prefix_len = nat64prefixes[i].prefix_len;
124
125 char *ptrv4 = (char *)__DECONST(struct in_addr *__indexable, addrv4);
126 char *ptr = (char *)__DECONST(struct in6_addr *__indexable, addr);
127
128 if (IN_ZERONET(ntohl(addrv4->s_addr)) || // 0.0.0.0/8 Source hosts on local network
129 IN_LOOPBACK(ntohl(addrv4->s_addr)) || // 127.0.0.0/8 Loopback
130 IN_LINKLOCAL(ntohl(addrv4->s_addr)) || // 169.254.0.0/16 Link Local
131 IN_DS_LITE(ntohl(addrv4->s_addr)) || // 192.0.0.0/29 DS-Lite
132 IN_6TO4_RELAY_ANYCAST(ntohl(addrv4->s_addr)) || // 192.88.99.0/24 6to4 Relay Anycast
133 IN_MULTICAST(ntohl(addrv4->s_addr)) || // 224.0.0.0/4 Multicast
134 INADDR_BROADCAST == addrv4->s_addr) { // 255.255.255.255/32 Limited Broadcast
135 return -1;
136 }
137
138 /* Check for the well-known prefix */
139 if (prefix_len == NAT64_PREFIX_LEN_96 &&
140 IN6_ARE_ADDR_EQUAL(&prefix, &well_known_prefix)) { // https://tools.ietf.org/html/rfc6052#section-3.1
141 if (IN_PRIVATE(ntohl(addrv4->s_addr)) || // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 Private-Use
142 IN_SHARED_ADDRESS_SPACE(ntohl(addrv4->s_addr))) { // 100.64.0.0/10 Shared Address Space
143 return -1;
144 }
145 }
146
147 memcpy(ptr, (char *)&prefix, prefix_len);
148
149 switch (prefix_len) {
150 case NAT64_PREFIX_LEN_96:
151 memcpy(ptr + 12, ptrv4, 4);
152 break;
153 case NAT64_PREFIX_LEN_64:
154 memcpy(ptr + 9, ptrv4, 4);
155 break;
156 case NAT64_PREFIX_LEN_56:
157 memcpy(ptr + 7, ptrv4, 1);
158 memcpy(ptr + 9, ptrv4 + 1, 3);
159 break;
160 case NAT64_PREFIX_LEN_48:
161 memcpy(ptr + 6, ptrv4, 2);
162 memcpy(ptr + 9, ptrv4 + 2, 2);
163 break;
164 case NAT64_PREFIX_LEN_40:
165 memcpy(ptr + 5, ptrv4, 3);
166 memcpy(ptr + 9, ptrv4 + 3, 1);
167 break;
168 case NAT64_PREFIX_LEN_32:
169 memcpy(ptr + 4, ptrv4, 4);
170 break;
171 default:
172 panic("NAT64-prefix len is wrong: %u", prefix_len);
173 }
174
175 if (clat_debug) {
176 char buf[MAX_IPv6_STR_LEN];
177 clat_log2((LOG_DEBUG, "%s synthesized %s\n", __func__,
178 inet_ntop(AF_INET6, (void *)addr, buf, sizeof(buf))));
179 }
180
181 return error;
182 }
183
184 /* Synthesize ipv4 from ipv6 */
185 int
nat464_synthesize_ipv4(ifnet_t ifp,const struct in6_addr * addr,struct in_addr * addrv4,bool * translate_p)186 nat464_synthesize_ipv4(ifnet_t ifp, const struct in6_addr *addr,
187 struct in_addr *addrv4, bool * translate_p)
188 {
189 struct ipv6_prefix nat64prefixes[NAT64_MAX_NUM_PREFIXES];
190 int error = 0, i = 0;
191 bool translate = false;
192
193 /* Below call is not optimized as it creates a copy of prefixes */
194 if ((error = ifnet_get_nat64prefix(ifp, nat64prefixes)) != 0) {
195 goto done;
196 }
197
198 for (i = 0; i < NAT64_MAX_NUM_PREFIXES; i++) {
199 if (nat64prefixes[i].prefix_len != 0) {
200 break;
201 }
202 }
203
204 VERIFY(i < NAT64_MAX_NUM_PREFIXES);
205
206 struct in6_addr prefix = nat64prefixes[i].ipv6_prefix;
207 int prefix_len = nat64prefixes[i].prefix_len;
208
209 VERIFY(prefix_len < sizeof(prefix));
210
211 char *ptrv4 = (char *)__DECONST(struct in_addr *__indexable, addrv4);
212 char *ptr = (char *)__DECONST(struct in6_addr *__indexable, addr);
213
214 /* -fbounds-safety:
215 * Override a warning about prefix_len being > 16 because
216 * we already checked that above.
217 */
218 if (memcmp((const struct in6_addr *__indexable)addr, &prefix, prefix_len) != 0) {
219 /* it's not the NAT64 prefix, so let it pass */
220 goto done;
221 }
222
223 switch (prefix_len) {
224 case NAT64_PREFIX_LEN_96:
225 memcpy(ptrv4, ptr + 12, 4);
226 break;
227 case NAT64_PREFIX_LEN_64:
228 memcpy(ptrv4, ptr + 9, 4);
229 break;
230 case NAT64_PREFIX_LEN_56:
231 memcpy(ptrv4, ptr + 7, 1);
232 memcpy(ptrv4 + 1, ptr + 9, 3);
233 break;
234 case NAT64_PREFIX_LEN_48:
235 memcpy(ptrv4, ptr + 6, 2);
236 memcpy(ptrv4 + 2, ptr + 9, 2);
237 break;
238 case NAT64_PREFIX_LEN_40:
239 memcpy(ptrv4, ptr + 5, 3);
240 memcpy(ptrv4 + 3, ptr + 9, 1);
241 break;
242 case NAT64_PREFIX_LEN_32:
243 memcpy(ptrv4, ptr + 4, 4);
244 break;
245 default:
246 panic("NAT64-prefix len is wrong: %u",
247 prefix_len);
248 }
249
250 if (clat_debug) {
251 char buf[MAX_IPv4_STR_LEN];
252 clat_log2((LOG_DEBUG, "%s desynthesized to %s\n", __func__,
253 inet_ntop(AF_INET, (void *)addrv4, buf, sizeof(buf))));
254 }
255 translate = true;
256 done:
257 *translate_p = translate;
258 return error;
259 }
260
261 #define PTR_IP(field) ((int32_t)offsetof(struct ip, field))
262 #define PTR_IP6(field) ((int32_t)offsetof(struct ip6_hdr, field))
263
264 /*
265 * Translate the ICMP header
266 */
267 int
nat464_translate_icmp(int naf,void * arg)268 nat464_translate_icmp(int naf, void *arg)
269 {
270 struct icmp *__single icmp4;
271 struct icmp6_hdr *__single icmp6;
272 uint32_t mtu;
273 int32_t ptr = -1;
274 uint8_t type;
275 uint8_t code;
276
277 switch (naf) {
278 case AF_INET:
279 icmp6 = arg;
280 type = icmp6->icmp6_type;
281 code = icmp6->icmp6_code;
282 mtu = ntohl(icmp6->icmp6_mtu);
283
284 switch (type) {
285 case ICMP6_ECHO_REQUEST:
286 type = ICMP_ECHO;
287 break;
288 case ICMP6_ECHO_REPLY:
289 type = ICMP_ECHOREPLY;
290 break;
291 case ICMP6_DST_UNREACH:
292 type = ICMP_UNREACH;
293 switch (code) {
294 case ICMP6_DST_UNREACH_NOROUTE:
295 case ICMP6_DST_UNREACH_BEYONDSCOPE:
296 case ICMP6_DST_UNREACH_ADDR:
297 code = ICMP_UNREACH_HOST;
298 break;
299 case ICMP6_DST_UNREACH_ADMIN:
300 code = ICMP_UNREACH_HOST_PROHIB;
301 break;
302 case ICMP6_DST_UNREACH_NOPORT:
303 code = ICMP_UNREACH_PORT;
304 break;
305 default:
306 return -1;
307 }
308 break;
309 case ICMP6_PACKET_TOO_BIG:
310 type = ICMP_UNREACH;
311 code = ICMP_UNREACH_NEEDFRAG;
312 mtu -= 20;
313 break;
314 case ICMP6_TIME_EXCEEDED:
315 type = ICMP_TIMXCEED;
316 break;
317 case ICMP6_PARAM_PROB:
318 switch (code) {
319 case ICMP6_PARAMPROB_HEADER:
320 type = ICMP_PARAMPROB;
321 code = ICMP_PARAMPROB_ERRATPTR;
322 ptr = ntohl(icmp6->icmp6_pptr);
323
324 if (ptr == PTR_IP6(ip6_vfc)) {
325 ; /* preserve */
326 } else if (ptr == PTR_IP6(ip6_vfc) + 1) {
327 ptr = PTR_IP(ip_tos);
328 } else if (ptr == PTR_IP6(ip6_plen) ||
329 ptr == PTR_IP6(ip6_plen) + 1) {
330 ptr = PTR_IP(ip_len);
331 } else if (ptr == PTR_IP6(ip6_nxt)) {
332 ptr = PTR_IP(ip_p);
333 } else if (ptr == PTR_IP6(ip6_hlim)) {
334 ptr = PTR_IP(ip_ttl);
335 } else if (ptr >= PTR_IP6(ip6_src) &&
336 ptr < PTR_IP6(ip6_dst)) {
337 ptr = PTR_IP(ip_src);
338 } else if (ptr >= PTR_IP6(ip6_dst) &&
339 ptr < (int32_t)sizeof(struct ip6_hdr)) {
340 ptr = PTR_IP(ip_dst);
341 } else {
342 return -1;
343 }
344 break;
345 case ICMP6_PARAMPROB_NEXTHEADER:
346 type = ICMP_UNREACH;
347 code = ICMP_UNREACH_PROTOCOL;
348 break;
349 default:
350 return -1;
351 }
352 break;
353 default:
354 return -1;
355 }
356 icmp6->icmp6_type = type;
357 icmp6->icmp6_code = code;
358 /* aligns well with a icmpv4 nextmtu */
359 icmp6->icmp6_mtu = htonl(mtu);
360 /* icmpv4 pptr is a one most significant byte */
361 if (ptr >= 0) {
362 icmp6->icmp6_pptr = htonl(ptr << 24);
363 }
364 break;
365
366 case AF_INET6:
367 icmp4 = arg;
368 type = icmp4->icmp_type;
369 code = icmp4->icmp_code;
370 mtu = ntohs(icmp4->icmp_nextmtu);
371
372 switch (type) {
373 case ICMP_ECHO:
374 type = ICMP6_ECHO_REQUEST;
375 break;
376 case ICMP_ECHOREPLY:
377 type = ICMP6_ECHO_REPLY;
378 break;
379 case ICMP_UNREACH:
380 type = ICMP6_DST_UNREACH;
381 switch (code) {
382 case ICMP_UNREACH_NET:
383 case ICMP_UNREACH_HOST:
384 case ICMP_UNREACH_NET_UNKNOWN:
385 case ICMP_UNREACH_HOST_UNKNOWN:
386 case ICMP_UNREACH_ISOLATED:
387 case ICMP_UNREACH_TOSNET:
388 case ICMP_UNREACH_TOSHOST:
389 code = ICMP6_DST_UNREACH_NOROUTE;
390 break;
391 case ICMP_UNREACH_PORT:
392 code = ICMP6_DST_UNREACH_NOPORT;
393 break;
394 case ICMP_UNREACH_NET_PROHIB:
395 case ICMP_UNREACH_HOST_PROHIB:
396 case ICMP_UNREACH_FILTER_PROHIB:
397 case ICMP_UNREACH_PRECEDENCE_CUTOFF:
398 code = ICMP6_DST_UNREACH_ADMIN;
399 break;
400 case ICMP_UNREACH_PROTOCOL:
401 type = ICMP6_PARAM_PROB;
402 code = ICMP6_PARAMPROB_NEXTHEADER;
403 ptr = offsetof(struct ip6_hdr, ip6_nxt);
404 break;
405 case ICMP_UNREACH_NEEDFRAG:
406 type = ICMP6_PACKET_TOO_BIG;
407 code = 0;
408 /*
409 * Make sure we don't overflow adjusting for
410 * translation overhead.
411 * If we do, just work with a lower mtu as is.
412 */
413 if (mtu <= (UINT16_MAX - CLAT46_HDR_EXPANSION_OVERHD)) {
414 mtu += CLAT46_HDR_EXPANSION_OVERHD;
415 }
416 break;
417 default:
418 return -1;
419 }
420 break;
421 case ICMP_TIMXCEED:
422 type = ICMP6_TIME_EXCEEDED;
423 break;
424 case ICMP_PARAMPROB:
425 type = ICMP6_PARAM_PROB;
426 switch (code) {
427 case ICMP_PARAMPROB_ERRATPTR:
428 code = ICMP6_PARAMPROB_HEADER;
429 break;
430 case ICMP_PARAMPROB_LENGTH:
431 code = ICMP6_PARAMPROB_HEADER;
432 break;
433 default:
434 return -1;
435 }
436
437 ptr = icmp4->icmp_pptr;
438 if (ptr == 0 || ptr == PTR_IP(ip_tos)) {
439 ; /* preserve */
440 } else if (ptr == PTR_IP(ip_len) ||
441 ptr == PTR_IP(ip_len) + 1) {
442 ptr = PTR_IP6(ip6_plen);
443 } else if (ptr == PTR_IP(ip_ttl)) {
444 ptr = PTR_IP6(ip6_hlim);
445 } else if (ptr == PTR_IP(ip_p)) {
446 ptr = PTR_IP6(ip6_nxt);
447 } else if (ptr >= PTR_IP(ip_src) &&
448 ptr < PTR_IP(ip_dst)) {
449 ptr = PTR_IP6(ip6_src);
450 } else if (ptr >= PTR_IP(ip_dst) &&
451 ptr < (int32_t)sizeof(struct ip)) {
452 ptr = PTR_IP6(ip6_dst);
453 } else {
454 return -1;
455 }
456 break;
457 default:
458 return -1;
459 }
460 icmp4->icmp_type = type;
461 icmp4->icmp_code = code;
462 icmp4->icmp_nextmtu = htons((uint16_t)mtu);
463
464 if (ptr >= 0) {
465 icmp4->icmp_void = htonl(ptr);
466 }
467 break;
468 }
469
470 return 0;
471 }
472
473 /*
474 * @brief This routine is called to perform address family translation on the
475 * inner IP header (that may come as payload) of an ICMP(v4/v6) error
476 * response.
477 *
478 * @param pbuf Pointer to packet buffer
479 * @param off Points to end of ICMP header
480 * @param tot_len Pointer to total length of the outer IP header
481 * @param off2 Points to end of inner IP header
482 * @param proto2 Inner IP proto field
483 * @param ttl2 Inner IP ttl field
484 * @param tot_len2 Inner IP total length
485 * @param src Pointer to the generic v4/v6 src address
486 * @param dst Pointer to the generic v4/v6 dst address
487 * @param af Old protocol family
488 * @param naf New protocol family
489 *
490 * @return -1 on error and 0 on success
491 */
492 int
nat464_translate_icmp_ip(pbuf_t * pbuf,uint16_t off,uint16_t * tot_len,uint16_t * off2,uint8_t proto2,uint8_t ttl2,uint16_t tot_len2,struct nat464_addr * src,struct nat464_addr * dst,protocol_family_t af,protocol_family_t naf)493 nat464_translate_icmp_ip(pbuf_t *pbuf, uint16_t off, uint16_t *tot_len, uint16_t *off2,
494 uint8_t proto2, uint8_t ttl2, uint16_t tot_len2, struct nat464_addr *src,
495 struct nat464_addr *dst, protocol_family_t af, protocol_family_t naf)
496 {
497 struct ip *__single ip4 = NULL;
498 struct ip6_hdr *__single ip6 = NULL;
499 void *__single hdr = NULL;
500 int hlen = 0, olen = 0;
501 uint64_t ipid_salt = (uint64_t)pbuf_get_packet_buffer_address(pbuf);
502
503 if (af == naf || (af != AF_INET && af != AF_INET6) ||
504 (naf != AF_INET && naf != AF_INET6)) {
505 return -1;
506 }
507
508 /* old header */
509 olen = *off2 - off;
510 /* new header */
511 hlen = naf == PF_INET ? sizeof(*ip4) : sizeof(*ip6);
512
513 /* Modify the pbuf to accommodate the new header */
514 hdr = pbuf_resize_segment(pbuf, off, olen, hlen);
515 if (hdr == NULL) {
516 return -1;
517 }
518
519 /* translate inner ip/ip6 header */
520 switch (naf) {
521 case AF_INET:
522 ip4 = hdr;
523 bzero(ip4, sizeof(*ip4));
524 ip4->ip_v = IPVERSION;
525 ip4->ip_hl = sizeof(*ip4) >> 2;
526 ip4->ip_len = htons((uint16_t)(sizeof(*ip4) + tot_len2 - olen));
527 ip4->ip_id = rfc6864 ? 0 : htons(ip_randomid(ipid_salt));
528 ip4->ip_off = htons(IP_DF);
529 ip4->ip_ttl = ttl2;
530 if (proto2 == IPPROTO_ICMPV6) {
531 ip4->ip_p = IPPROTO_ICMP;
532 } else {
533 ip4->ip_p = proto2;
534 }
535 ip4->ip_src = src->natv4addr;
536 ip4->ip_dst = dst->natv4addr;
537 ip4->ip_sum = pbuf_inet_cksum(pbuf, 0, 0, ip4->ip_hl << 2);
538
539 if (clat_debug) {
540 char buf[MAX_IPv4_STR_LEN];
541 clat_log2((LOG_DEBUG, "%s translated to IPv4 (inner) "
542 "ip_len: %#x ip_p: %d ip_sum: %#x ip_src: %s ip_dst: %s \n",
543 __func__, ntohs(ip4->ip_len), ip4->ip_p, ntohs(ip4->ip_sum),
544 inet_ntop(AF_INET, (void *)&ip4->ip_src, buf, sizeof(buf)),
545 inet_ntop(AF_INET, (void *)&ip4->ip_dst, buf, sizeof(buf))));
546 }
547 break;
548 case AF_INET6:
549 ip6 = hdr;
550 bzero(ip6, sizeof(*ip6));
551 ip6->ip6_vfc = IPV6_VERSION;
552 ip6->ip6_plen = htons((uint16_t)(tot_len2 - olen));
553 if (proto2 == IPPROTO_ICMP) {
554 ip6->ip6_nxt = IPPROTO_ICMPV6;
555 } else {
556 ip6->ip6_nxt = proto2;
557 }
558 if (!ttl2 || ttl2 > IPV6_DEFHLIM) {
559 ip6->ip6_hlim = IPV6_DEFHLIM;
560 } else {
561 ip6->ip6_hlim = ttl2;
562 }
563 ip6->ip6_src = src->natv6addr;
564 ip6->ip6_dst = dst->natv6addr;
565
566 if (clat_debug) {
567 char buf2[MAX_IPv6_STR_LEN];
568 clat_log2((LOG_DEBUG, "%s translated to IPv6 (inner) "
569 "ip6_plen: %#x ip6_nxt: %d ip6_src: %s ip6_dst: %s \n",
570 __func__, ntohs(ip6->ip6_plen), ip6->ip6_nxt,
571 inet_ntop(AF_INET6, (void *)&ip6->ip6_src, buf2, sizeof(buf2)),
572 inet_ntop(AF_INET6, (void *)&ip6->ip6_dst, buf2, sizeof(buf2))));
573 }
574 break;
575 }
576
577 /* adjust payload offset and total packet length */
578 *off2 += hlen - olen;
579 *tot_len += hlen - olen;
580
581 return 0;
582 }
583 /*
584 * @brief The function inserts IPv6 fragmentation header
585 * and populates it with the passed parameters.
586 *
587 * @param pbuf Pointer to the packet buffer
588 * @param ip_id IP identifier (in network byte order)
589 * @param frag_offset Fragment offset (in network byte order)
590 * @param is_last_frag Boolean indicating if the fragment header is for
591 * last fragment or not.
592 *
593 * @return -1 on error and 0 on success.
594 */
595 int
nat464_insert_frag46(pbuf_t * pbuf,uint16_t ip_id_val,uint16_t frag_offset,boolean_t is_last_frag)596 nat464_insert_frag46(pbuf_t *pbuf, uint16_t ip_id_val, uint16_t frag_offset,
597 boolean_t is_last_frag)
598 {
599 struct ip6_frag *p_ip6_frag = NULL;
600 struct ip6_hdr *p_ip6h = NULL;
601
602 /* Insert IPv6 fragmentation header */
603 if (pbuf_resize_segment(pbuf, sizeof(struct ip6_hdr), 0,
604 sizeof(struct ip6_frag)) == NULL) {
605 return -1;
606 }
607
608 p_ip6h = mtod(pbuf->pb_mbuf, struct ip6_hdr *);
609 p_ip6_frag = (struct ip6_frag *)pbuf_contig_segment(pbuf,
610 sizeof(struct ip6_hdr), sizeof(struct ip6_frag));
611
612 if (p_ip6_frag == NULL) {
613 return -1;
614 }
615
616 /* Populate IPv6 fragmentation header */
617 p_ip6_frag->ip6f_nxt = p_ip6h->ip6_nxt;
618 p_ip6_frag->ip6f_reserved = 0;
619 p_ip6_frag->ip6f_offlg = (uint16_t)(frag_offset << 3);
620 if (!is_last_frag) {
621 p_ip6_frag->ip6f_offlg |= 0x1;
622 }
623 p_ip6_frag->ip6f_offlg = htons(p_ip6_frag->ip6f_offlg);
624 p_ip6_frag->ip6f_ident = ip_id_val;
625
626 /* Update IPv6 header */
627 p_ip6h->ip6_nxt = IPPROTO_FRAGMENT;
628 p_ip6h->ip6_plen = htons(ntohs(p_ip6h->ip6_plen) +
629 sizeof(struct ip6_frag));
630
631 return 0;
632 }
633
634 int
nat464_translate_64(pbuf_t * pbuf,int off,uint8_t tos,uint8_t * proto,uint8_t ttl,struct in_addr src_v4,struct in_addr dst_v4,uint64_t tot_len,boolean_t * p_is_first_frag)635 nat464_translate_64(pbuf_t *pbuf, int off, uint8_t tos,
636 uint8_t *proto, uint8_t ttl, struct in_addr src_v4,
637 struct in_addr dst_v4, uint64_t tot_len, boolean_t *p_is_first_frag)
638 {
639 struct ip *ip4;
640 struct ip6_frag *p_frag6 = NULL;
641 struct ip6_frag frag6 = {};
642 boolean_t is_frag = FALSE;
643 uint16_t ip_frag_off = 0;
644
645 /*
646 * ip_input asserts for rcvif to be not NULL
647 * That may not be true for two corner cases
648 * 1. If for some reason a local app sends DNS
649 * AAAA query to local host
650 * 2. If IPv6 stack in kernel internally generates a
651 * message destined for a synthesized IPv6 end-point.
652 */
653 if (pbuf->pb_ifp == NULL) {
654 return NT_DROP;
655 }
656
657 if (*proto == IPPROTO_FRAGMENT) {
658 p_frag6 = (struct ip6_frag *)pbuf_contig_segment(pbuf,
659 sizeof(struct ip6_hdr), sizeof(struct ip6_frag));
660 if (p_frag6 == NULL) {
661 ip6stat.ip6s_clat464_in_64frag_transfail_drop++;
662 return NT_DROP;
663 }
664
665 frag6 = *p_frag6;
666 p_frag6 = NULL;
667 *proto = frag6.ip6f_nxt;
668 off += sizeof(struct ip6_frag);
669 is_frag = TRUE;
670 ip_frag_off = (ntohs(frag6.ip6f_offlg & IP6F_OFF_MASK)) >> 3;
671 if (ip_frag_off != 0) {
672 *p_is_first_frag = FALSE;
673 }
674 }
675
676 ip4 = (struct ip *)pbuf_resize_segment(pbuf, 0, off, sizeof(*ip4));
677 if (ip4 == NULL) {
678 return NT_DROP;
679 }
680 ip4->ip_v = 4;
681 ip4->ip_hl = 5;
682 ip4->ip_tos = tos;
683 ip4->ip_len = htons((uint16_t)(sizeof(*ip4) + (tot_len - off)));
684 ip4->ip_id = 0;
685 ip4->ip_off = 0;
686 ip4->ip_ttl = ttl;
687 ip4->ip_p = *proto;
688 ip4->ip_sum = 0;
689 ip4->ip_src = src_v4;
690 ip4->ip_dst = dst_v4;
691 if (is_frag) {
692 /*
693 * https://tools.ietf.org/html/rfc7915#section-5.1.1
694 * Identification: Copied from the low-order 16 bits in the
695 * Identification field in the Fragment Header.
696 */
697 ip4->ip_id = ntohl(frag6.ip6f_ident) & 0xffff;
698 ip4->ip_id = htons(ip4->ip_id);
699 if (frag6.ip6f_offlg & IP6F_MORE_FRAG) {
700 ip_frag_off |= IP_MF;
701 }
702 ip4->ip_off = htons(ip_frag_off);
703 } else {
704 ip4->ip_off |= htons(IP_DF);
705 }
706
707 /*
708 * Defer calculating ip_sum for ICMPv6 as we do it
709 * later in Protocol translation
710 */
711 if (*proto != IPPROTO_ICMPV6) {
712 ip4->ip_sum = pbuf_inet_cksum(pbuf, 0, 0, ip4->ip_hl << 2);
713 }
714
715 if (clat_debug) {
716 char buf1[MAX_IPv4_STR_LEN], buf2[MAX_IPv4_STR_LEN];
717 clat_log2((LOG_DEBUG, "%s translated to IPv4 ip_len: %#x "
718 "ip_p: %d ip_sum: %#x ip_src: %s ip_dst: %s \n", __func__,
719 ntohs(ip4->ip_len), ip4->ip_p, ntohs(ip4->ip_sum),
720 inet_ntop(AF_INET, (void *)&ip4->ip_src, buf1, sizeof(buf1)),
721 inet_ntop(AF_INET, (void *)&ip4->ip_dst, buf2, sizeof(buf2))));
722 }
723 return NT_NAT64;
724 }
725 /*
726 * @brief The routine translates the IPv4 header to IPv6 header.
727 *
728 * @param pbuf Pointer to the generic packet buffer
729 * @param off Offset to the end of IP header
730 * @param tos Type of service
731 * @param proto Protocol running over IP
732 * @param ttl Time to live
733 * @param src_v6 Source IPv6 address
734 * @param dst_v6 Destination IPv6 address
735 * @param tot_len Total payload length
736 *
737 * @return NT_NAT64 if IP header translation is successful, else error
738 */
739 int
nat464_translate_46(pbuf_t * pbuf,uint16_t off,uint8_t tos,uint8_t proto,uint8_t ttl,struct in6_addr src_v6,struct in6_addr dst_v6,uint16_t tot_len)740 nat464_translate_46(pbuf_t *pbuf, uint16_t off, uint8_t tos,
741 uint8_t proto, uint8_t ttl, struct in6_addr src_v6,
742 struct in6_addr dst_v6, uint16_t tot_len)
743 {
744 struct ip6_hdr *ip6;
745
746 if (pbuf->pb_ifp == NULL) {
747 return NT_DROP;
748 }
749
750 /*
751 * Trim the buffer from head of size equal to to off (which is equal to
752 * the size of IP header and prepend IPv6 header length to the buffer
753 */
754 ip6 = (struct ip6_hdr *)pbuf_resize_segment(pbuf, 0, off, sizeof(*ip6));
755 if (ip6 == NULL) {
756 return NT_DROP;
757 }
758 ip6->ip6_flow = htonl((6 << 28) | (tos << 20));
759 ip6->ip6_plen = htons(tot_len - off);
760 ip6->ip6_nxt = proto;
761 ip6->ip6_hlim = ttl;
762 ip6->ip6_src = src_v6;
763 ip6->ip6_dst = dst_v6;
764
765 if (clat_debug) {
766 char buf1[MAX_IPv6_STR_LEN], buf2[MAX_IPv6_STR_LEN];
767 clat_log2((LOG_DEBUG, "%s translated to IPv6 ip6_plen: %#x "
768 " ip6_nxt: %d ip6_src: %s ip6_dst: %s \n", __func__,
769 ntohs(ip6->ip6_plen), ip6->ip6_nxt,
770 inet_ntop(AF_INET6, (void *)&ip6->ip6_src, buf1, sizeof(buf1)),
771 inet_ntop(AF_INET6, (void *)&ip6->ip6_dst, buf2, sizeof(buf2))));
772 }
773 return NT_NAT64;
774 }
775
776 /* Handle the next protocol checksum */
777 /*
778 * @brief This routine translates the Proto running over IP and updates the checksum
779 * for IP header translation. It also updates pbuf checksum flags and related fields.
780 *
781 * @param pbuf Pointer to protocol buffer
782 * @param nsrc New source address
783 * @param ndst New destination address
784 * @param af Old family
785 * @param naf New family
786 *
787 * @return void
788 */
789 int
nat464_translate_proto(pbuf_t * pbuf,struct nat464_addr * osrc,struct nat464_addr * odst,uint8_t oproto,protocol_family_t af,protocol_family_t naf,int direction,boolean_t only_csum)790 nat464_translate_proto(pbuf_t *pbuf, struct nat464_addr *osrc,
791 struct nat464_addr *odst, uint8_t oproto, protocol_family_t af,
792 protocol_family_t naf, int direction, boolean_t only_csum)
793 {
794 struct ip *iph = NULL;
795 struct ip6_hdr *ip6h = NULL;
796 uint16_t hlen = 0, plen = 0;
797 uint16_t tot_len = 0;
798 void *nsrc = NULL, *ndst = NULL;
799 uint8_t *proto = 0;
800 uint16_t *psum = NULL;
801 boolean_t do_ones_complement = FALSE;
802
803 /* For now these routines only support 464 translations */
804 VERIFY(af != naf);
805 VERIFY(af == PF_INET || af == PF_INET6);
806
807 /*
808 * For now out must be for v4 to v6 translation
809 * and in must be for v6 to v4 translation.
810 */
811 switch (naf) {
812 case PF_INET: {
813 iph = pbuf->pb_data;
814 hlen = (uint16_t)(iph->ip_hl << 2);
815 plen = ntohs(iph->ip_len) - hlen;
816 tot_len = ntohs(iph->ip_len);
817 nsrc = &iph->ip_src;
818 ndst = &iph->ip_dst;
819 proto = &iph->ip_p;
820 break;
821 }
822 case PF_INET6: {
823 ip6h = pbuf->pb_data;
824 hlen = (uint16_t)sizeof(*ip6h);
825 plen = ntohs(ip6h->ip6_plen);
826 tot_len = hlen + plen;
827 nsrc = &ip6h->ip6_src;
828 ndst = &ip6h->ip6_dst;
829 proto = &ip6h->ip6_nxt;
830 break;
831 }
832 default:
833 return NT_DROP; /* We should never come here */
834 }
835
836 if (*proto != oproto) {
837 return NT_DROP;
838 }
839
840 /*
841 * We may want to manipulate csum flags in some cases
842 * and not act on the protocol header as it may not
843 * carry protocol checksums.
844 * For example, fragments other than the first one would
845 * not carry protocol headers.
846 */
847 if (only_csum) {
848 /*
849 * Only translate ICMP proto in the header
850 * and adjust checksums
851 */
852 if (*proto == IPPROTO_ICMP) {
853 if (naf != PF_INET6) {
854 return NT_DROP;
855 }
856
857 *proto = IPPROTO_ICMPV6;
858 } else if (*proto == IPPROTO_ICMPV6) {
859 if (naf != PF_INET) {
860 return NT_DROP;
861 }
862
863 *proto = IPPROTO_ICMP;
864 /* Recalculate IP checksum as proto field has changed */
865 iph->ip_sum = 0;
866 iph->ip_sum = pbuf_inet_cksum(pbuf, 0, 0, hlen);
867 }
868 goto done;
869 }
870
871 switch (*proto) {
872 case IPPROTO_UDP: {
873 struct udphdr *uh = (struct udphdr *)pbuf_contig_segment(pbuf, hlen,
874 sizeof(*uh));
875
876 if (uh == NULL) {
877 return NT_DROP;
878 }
879
880 if (!(*pbuf->pb_csum_flags & (CSUM_UDP | CSUM_PARTIAL)) &&
881 uh->uh_sum == 0 && af == PF_INET && naf == PF_INET6) {
882 uh->uh_sum = pbuf_inet6_cksum(pbuf, IPPROTO_UDP,
883 hlen, ntohs(ip6h->ip6_plen));
884 if (uh->uh_sum == 0) {
885 uh->uh_sum = 0xffff;
886 }
887 goto done;
888 }
889
890 psum = &uh->uh_sum;
891 break;
892 }
893 case IPPROTO_TCP: {
894 struct tcphdr *th = (struct tcphdr *)pbuf_contig_segment(pbuf, hlen,
895 sizeof(*th));
896
897 if (th == NULL) {
898 return NT_DROP;
899 }
900
901 psum = &th->th_sum;
902 break;
903 }
904 }
905
906 /*
907 * Translate the protocol header, update IP header if needed,
908 * calculate checksums and update the checksum flags.
909 */
910 switch (*proto) {
911 case IPPROTO_UDP:
912 /* Fall through */
913 case IPPROTO_TCP:
914 {
915 /*
916 * If it is a locally generated and has CSUM flags set
917 * for TCP and UDP it means we have pseudo header checksum
918 * that has not yet been one's complemented.
919 */
920 if (direction == NT_OUT &&
921 (*pbuf->pb_csum_flags & CSUM_PARTIAL)) {
922 do_ones_complement = TRUE;
923 }
924
925 nat464_addr_cksum_fixup(psum, osrc, (struct nat464_addr *)nsrc,
926 af, naf, (*proto == IPPROTO_UDP) ? 1 : 0, do_ones_complement);
927 nat464_addr_cksum_fixup(psum, odst, (struct nat464_addr *)ndst,
928 af, naf, (*proto == IPPROTO_UDP) ? 1 : 0, do_ones_complement);
929
930 break;
931 }
932 case IPPROTO_ICMP: {
933 if (naf != PF_INET6) { /* allow only v6 as naf for ICMP */
934 return NT_DROP;
935 }
936
937 struct icmp *__single icmph = NULL;
938 struct icmp6_hdr *__single icmp6h = NULL;
939 uint16_t ip2off = 0, hlen2 = 0, tot_len2 = 0;
940
941 icmph = (struct icmp*) pbuf_contig_segment(pbuf, hlen,
942 ICMP_MINLEN);
943 if (icmph == NULL) {
944 return NT_DROP;
945 }
946
947 /* Translate the ICMP header */
948 if (nat464_translate_icmp(PF_INET6, icmph) != 0) {
949 return NT_DROP;
950 }
951
952 *proto = IPPROTO_ICMPV6;
953 icmp6h = (struct icmp6_hdr *__single)(void *)icmph;
954 pbuf_copy_back(pbuf, hlen, sizeof(struct icmp6_hdr),
955 icmp6h, sizeof(*icmp6h));
956
957 /*Translate the inner IP header only for error messages */
958 if (ICMP6_ERRORTYPE(icmp6h->icmp6_type)) {
959 ip2off = (uint16_t)(hlen + sizeof(*icmp6h));
960 struct ip *iph2 = NULL;
961 iph2 = (struct ip*) pbuf_contig_segment(pbuf, ip2off,
962 sizeof(*iph2));
963 if (iph2 == NULL) {
964 return NT_DROP;
965 }
966
967 hlen2 = (uint16_t)(ip2off + (iph2->ip_hl << 2));
968 tot_len2 = ntohs(iph2->ip_len);
969
970 /*
971 * Destination in outer IP should be Source in inner IP,
972 * otherwise the ICMP is likely errnoneous.
973 */
974 if (!IN_ARE_ADDR_EQUAL(&odst->natv4addr, &iph2->ip_src)) {
975 return NT_DROP;
976 }
977
978 if (nat464_translate_icmp_ip(pbuf, ip2off, &tot_len,
979 &hlen2, iph2->ip_p, iph2->ip_ttl, tot_len2,
980 (struct nat464_addr *)ndst, (struct nat464_addr *)nsrc,
981 PF_INET, PF_INET6) != 0) {
982 return NT_DROP;
983 }
984 /* Update total length/payload length for outer header */
985 switch (naf) {
986 case PF_INET:
987 iph->ip_len = htons(tot_len);
988 break;
989 case PF_INET6:
990 ip6h->ip6_plen = htons(tot_len - hlen);
991 break;
992 }
993 iph2 = NULL;
994 }
995
996 icmp6h->icmp6_cksum = 0;
997 icmp6h->icmp6_cksum = pbuf_inet6_cksum(pbuf, IPPROTO_ICMPV6, hlen,
998 ntohs(ip6h->ip6_plen));
999
1000 clat_log2((LOG_DEBUG, "%s translated to ICMPV6 type: %d "
1001 "code: %d checksum: %#x \n", __func__, icmp6h->icmp6_type,
1002 icmp6h->icmp6_code, icmp6h->icmp6_cksum));
1003
1004 icmph = NULL;
1005 icmp6h = NULL;
1006 break;
1007 }
1008 case IPPROTO_ICMPV6:
1009 { if (naf != PF_INET) { /* allow only v4 as naf for ICMPV6 */
1010 return NT_DROP;
1011 }
1012
1013 struct icmp6_hdr *__single icmp6h = NULL;
1014 struct icmp *__single icmph = NULL;
1015 uint16_t ip2off = 0, hlen2 = 0, tot_len2 = 0;
1016
1017 icmp6h = (struct icmp6_hdr*) pbuf_contig_segment(pbuf, hlen,
1018 sizeof(*icmp6h));
1019 if (icmp6h == NULL) {
1020 return NT_DROP;
1021 }
1022
1023 /* Translate the ICMP header */
1024 if (nat464_translate_icmp(PF_INET, icmp6h) != 0) {
1025 return NT_DROP;
1026 }
1027
1028 *proto = IPPROTO_ICMP;
1029 icmph = (struct icmp *__single)(void *)icmp6h;
1030 pbuf_copy_back(pbuf, hlen, ICMP_MINLEN,
1031 icmph, sizeof(*icmph));
1032
1033 /*Translate the inner IP header only for error messages */
1034 if (ICMP_ERRORTYPE(icmph->icmp_type)) {
1035 ip2off = hlen + ICMP_MINLEN;
1036 struct ip6_hdr *iph2 = NULL;
1037 iph2 = (struct ip6_hdr*) pbuf_contig_segment(pbuf, ip2off,
1038 sizeof(*iph2));
1039 if (iph2 == NULL) {
1040 return NT_DROP;
1041 }
1042
1043 /* hlen2 points to end of inner IP header from the beginning */
1044 hlen2 = ip2off + sizeof(struct ip6_hdr);
1045 tot_len2 = ntohs(iph2->ip6_plen) + sizeof(struct ip6_hdr);
1046
1047 if (nat464_translate_icmp_ip(pbuf, ip2off, &tot_len,
1048 &hlen2, iph2->ip6_nxt, iph2->ip6_hlim, tot_len2,
1049 (struct nat464_addr *)ndst, (struct nat464_addr *)nsrc,
1050 PF_INET6, PF_INET) != 0) {
1051 return NT_DROP;
1052 }
1053
1054 /* Update total length for outer header */
1055 switch (naf) {
1056 case PF_INET:
1057 iph->ip_len = htons(tot_len);
1058 break;
1059 case PF_INET6:
1060 ip6h->ip6_plen = htons(tot_len - hlen);
1061 break;
1062 }
1063 iph2 = NULL;
1064 }
1065 /* Recalculate IP checksum as some IP fields might have changed */
1066 iph->ip_sum = 0;
1067 iph->ip_sum = pbuf_inet_cksum(pbuf, 0, 0, iph->ip_hl << 2);
1068 icmph->icmp_cksum = 0;
1069 icmph->icmp_cksum = pbuf_inet_cksum(pbuf, 0, hlen,
1070 ntohs(iph->ip_len) - hlen);
1071
1072 clat_log2((LOG_DEBUG, "%s translated to ICMP type: %d "
1073 "code: %d checksum: %#x \n", __func__, icmph->icmp_type,
1074 icmph->icmp_code, icmph->icmp_cksum));
1075
1076 icmp6h = NULL;
1077 icmph = NULL;
1078 break;}
1079
1080 /*
1081 * https://tools.ietf.org/html/rfc7915#section-5.1.1
1082 * If the Next Header field of the Fragment Header is an
1083 * extension header (except ESP, but including the Authentication
1084 * Header (AH)), then the packet SHOULD be dropped and logged.
1085 */
1086 case IPPROTO_HOPOPTS:
1087 case IPPROTO_ROUTING:
1088 case IPPROTO_DSTOPTS:
1089 case IPPROTO_AH:
1090 return NT_DROP;
1091
1092 case IPPROTO_FRAGMENT:
1093 /*
1094 * The fragment header is appended after or removed before
1095 * calling into this routine.
1096 */
1097 VERIFY(FALSE);
1098 case IPPROTO_ESP:
1099 break;
1100
1101 default:
1102 return NT_DROP;
1103 }
1104
1105 done:
1106 /* Update checksum flags and offsets based on direction */
1107 if (direction == NT_OUT) {
1108 if ((*pbuf->pb_csum_flags & (CSUM_DATA_VALID | CSUM_PARTIAL)) ==
1109 (CSUM_DATA_VALID | CSUM_PARTIAL)) {
1110 (pbuf->pb_mbuf)->m_pkthdr.csum_tx_start += CLAT46_HDR_EXPANSION_OVERHD;
1111 (pbuf->pb_mbuf)->m_pkthdr.csum_tx_stuff += CLAT46_HDR_EXPANSION_OVERHD;
1112 }
1113
1114 if (*pbuf->pb_csum_flags & CSUM_TCP) {
1115 *pbuf->pb_csum_flags |= CSUM_TCPIPV6;
1116 }
1117 if (*pbuf->pb_csum_flags & CSUM_UDP) {
1118 *pbuf->pb_csum_flags |= CSUM_UDPIPV6;
1119 }
1120 if (*pbuf->pb_csum_flags & CSUM_FRAGMENT) {
1121 *pbuf->pb_csum_flags |= CSUM_FRAGMENT_IPV6;
1122 }
1123
1124 /* Clear IPv4 checksum flags */
1125 *pbuf->pb_csum_flags &= ~(CSUM_IP | CSUM_IP_FRAGS | CSUM_DELAY_DATA | CSUM_FRAGMENT);
1126 /*
1127 * If the packet requires TCP segmentation due to TSO offload,
1128 * then change the checksum flag to indicate that an IPv6
1129 * TCP segmentation is needed now.
1130 */
1131 if (*pbuf->pb_csum_flags & CSUM_TSO_IPV4) {
1132 *pbuf->pb_csum_flags &= ~CSUM_TSO_IPV4;
1133 *pbuf->pb_csum_flags |= CSUM_TSO_IPV6;
1134 }
1135 } else if (direction == NT_IN) {
1136 /* XXX On input just reset csum flags */
1137 *pbuf->pb_csum_flags = 0; /* Reset all flags for now */
1138 #if 0
1139 /* Update csum flags and offsets for rx */
1140 if (*pbuf->pb_csum_flags & CSUM_PARTIAL) {
1141 (pbuf->pb_mbuf)->m_pkthdr.csum_rx_start -= CLAT46_HDR_EXPANSION_OVERHD;
1142 }
1143 #endif
1144 }
1145 return NT_NAT64;
1146 }
1147
1148 /* Fix the proto checksum for address change */
1149 static void
nat464_addr_cksum_fixup(uint16_t * pc,struct nat464_addr * ao,struct nat464_addr * an,protocol_family_t af,protocol_family_t naf,uint8_t u,boolean_t do_ones_complement)1150 nat464_addr_cksum_fixup(uint16_t *pc, struct nat464_addr *ao, struct nat464_addr *an,
1151 protocol_family_t af, protocol_family_t naf, uint8_t u, boolean_t do_ones_complement)
1152 {
1153 /* Currently we only support v4 to v6 and vice versa */
1154 VERIFY(af != naf);
1155
1156 switch (af) {
1157 case PF_INET:
1158 switch (naf) {
1159 case PF_INET6:
1160 if (do_ones_complement) {
1161 *pc = ~nat464_cksum_fixup(nat464_cksum_fixup(
1162 nat464_cksum_fixup(nat464_cksum_fixup(nat464_cksum_fixup(
1163 nat464_cksum_fixup(nat464_cksum_fixup(nat464_cksum_fixup(~*pc,
1164 ao->nataddr16[0], an->nataddr16[0], u),
1165 ao->nataddr16[1], an->nataddr16[1], u),
1166 0, an->nataddr16[2], u),
1167 0, an->nataddr16[3], u),
1168 0, an->nataddr16[4], u),
1169 0, an->nataddr16[5], u),
1170 0, an->nataddr16[6], u),
1171 0, an->nataddr16[7], u);
1172 } else {
1173 *pc = nat464_cksum_fixup(nat464_cksum_fixup(
1174 nat464_cksum_fixup(nat464_cksum_fixup(nat464_cksum_fixup(
1175 nat464_cksum_fixup(nat464_cksum_fixup(nat464_cksum_fixup(*pc,
1176 ao->nataddr16[0], an->nataddr16[0], u),
1177 ao->nataddr16[1], an->nataddr16[1], u),
1178 0, an->nataddr16[2], u),
1179 0, an->nataddr16[3], u),
1180 0, an->nataddr16[4], u),
1181 0, an->nataddr16[5], u),
1182 0, an->nataddr16[6], u),
1183 0, an->nataddr16[7], u);
1184 }
1185 break;
1186 }
1187 break;
1188 case PF_INET6:
1189 /*
1190 * XXX For NAT464 this only applies to the incoming path.
1191 * The checksum therefore is already ones complemented.
1192 * Therefore we just perform normal fixup.
1193 */
1194 switch (naf) {
1195 case PF_INET:
1196 *pc = nat464_cksum_fixup(nat464_cksum_fixup(
1197 nat464_cksum_fixup(nat464_cksum_fixup(nat464_cksum_fixup(
1198 nat464_cksum_fixup(nat464_cksum_fixup(nat464_cksum_fixup(*pc,
1199 ao->nataddr16[0], an->nataddr16[0], u),
1200 ao->nataddr16[1], an->nataddr16[1], u),
1201 ao->nataddr16[2], 0, u),
1202 ao->nataddr16[3], 0, u),
1203 ao->nataddr16[4], 0, u),
1204 ao->nataddr16[5], 0, u),
1205 ao->nataddr16[6], 0, u),
1206 ao->nataddr16[7], 0, u);
1207 break;
1208 }
1209 break;
1210 }
1211 }
1212
1213 uint16_t
nat464_cksum_fixup(uint16_t cksum,uint16_t old,uint16_t new,uint8_t udp)1214 nat464_cksum_fixup(uint16_t cksum, uint16_t old, uint16_t new, uint8_t udp)
1215 {
1216 uint32_t l;
1217
1218 if (udp && !cksum) {
1219 return 0;
1220 }
1221 l = cksum + old - new;
1222 l = (l >> 16) + (l & 0xffff);
1223 l = l & 0xffff;
1224 if (udp && !l) {
1225 return 0xffff;
1226 }
1227 return (uint16_t)l;
1228 }
1229
1230 /* CLAT46 event handlers */
1231 void
in6_clat46_eventhdlr_callback(struct eventhandler_entry_arg arg0 __unused,in6_clat46_evhdlr_code_t in6_clat46_ev_code,pid_t epid,uuid_t euuid)1232 in6_clat46_eventhdlr_callback(struct eventhandler_entry_arg arg0 __unused,
1233 in6_clat46_evhdlr_code_t in6_clat46_ev_code, pid_t epid, uuid_t euuid)
1234 {
1235 struct kev_msg ev_msg;
1236 struct kev_netevent_clat46_data clat46_event_data;
1237
1238 bzero(&ev_msg, sizeof(ev_msg));
1239 bzero(&clat46_event_data, sizeof(clat46_event_data));
1240
1241 ev_msg.vendor_code = KEV_VENDOR_APPLE;
1242 ev_msg.kev_class = KEV_NETWORK_CLASS;
1243 ev_msg.kev_subclass = KEV_NETEVENT_SUBCLASS;
1244 ev_msg.event_code = KEV_NETEVENT_CLAT46_EVENT;
1245
1246 bzero(&clat46_event_data, sizeof(clat46_event_data));
1247 clat46_event_data.clat46_event_code = in6_clat46_ev_code;
1248 clat46_event_data.epid = epid;
1249 uuid_copy(clat46_event_data.euuid, euuid);
1250
1251 ev_msg.dv[0].data_ptr = &clat46_event_data;
1252 ev_msg.dv[0].data_length = sizeof(clat46_event_data);
1253
1254 kev_post_msg(&ev_msg);
1255 }
1256
1257 struct in6_clat46_event_nwk_wq_entry {
1258 struct nwk_wq_entry nwk_wqe;
1259 struct kev_netevent_clat46_data in6_clat46_ev_arg;
1260 };
1261
1262 static void
in6_clat46_event_callback(struct nwk_wq_entry * nwk_item)1263 in6_clat46_event_callback(struct nwk_wq_entry *nwk_item)
1264 {
1265 struct in6_clat46_event_nwk_wq_entry *p_ev;
1266
1267 p_ev = __container_of(nwk_item,
1268 struct in6_clat46_event_nwk_wq_entry, nwk_wqe);
1269
1270 EVENTHANDLER_INVOKE(&in6_clat46_evhdlr_ctxt, in6_clat46_event,
1271 p_ev->in6_clat46_ev_arg.clat46_event_code, p_ev->in6_clat46_ev_arg.epid,
1272 p_ev->in6_clat46_ev_arg.euuid);
1273
1274 kfree_type(struct in6_clat46_event_nwk_wq_entry, p_ev);
1275 }
1276
1277 void
in6_clat46_event_enqueue_nwk_wq_entry(in6_clat46_evhdlr_code_t in6_clat46_event_code,pid_t epid,uuid_t euuid)1278 in6_clat46_event_enqueue_nwk_wq_entry(in6_clat46_evhdlr_code_t in6_clat46_event_code,
1279 pid_t epid, uuid_t euuid)
1280 {
1281 struct in6_clat46_event_nwk_wq_entry *p_ev = NULL;
1282
1283 p_ev = kalloc_type(struct in6_clat46_event_nwk_wq_entry,
1284 Z_WAITOK | Z_ZERO | Z_NOFAIL);
1285
1286 p_ev->nwk_wqe.func = in6_clat46_event_callback;
1287 p_ev->in6_clat46_ev_arg.clat46_event_code = in6_clat46_event_code;
1288 p_ev->in6_clat46_ev_arg.epid = epid;
1289 uuid_copy(p_ev->in6_clat46_ev_arg.euuid, euuid);
1290
1291 evhlog(debug, "%s: eventhandler enqueuing event of type=in6_clat46_event event_code=%s",
1292 __func__, in6_clat46_evhdlr_code2str(in6_clat46_event_code));
1293
1294 nwk_wq_enqueue(&p_ev->nwk_wqe);
1295 }
1296
1297 extern const char*
in6_clat46_evhdlr_code2str(enum in6_clat46_evhdlr_code_t code)1298 in6_clat46_evhdlr_code2str(enum in6_clat46_evhdlr_code_t code)
1299 {
1300 switch (code) {
1301 #define CLAT46_CODE_TO_STRING(type) case type: return #type;
1302 CLAT46_CODE_TO_STRING(IN6_CLAT46_EVENT_V4_FLOW)
1303 CLAT46_CODE_TO_STRING(IN6_CLAT46_EVENT_V6_ADDR_CONFFAIL)
1304 #undef CLAT46_CODE_TO_STRING
1305 }
1306 return "UNKNOWN_IN6_CLAT46_EVHDLR_CODE";
1307 }
1308