]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/nat464_utils.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / bsd / net / nat464_utils.c
CommitLineData
d9a64523 1/*
f427ee49 2 * Copyright (c) 2018-2020 Apple Inc. All rights reserved.
d9a64523
A
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
89int clat_debug = 0;
90
91os_log_t nat_log_handle;
92
93static void
94nat464_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 */
98int
99nat464_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,
0a7de745
A
103 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104 0x00, 0x00, 0x00, 0x00},
d9a64523
A
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 */
0a7de745
A
110 if ((error = ifnet_get_nat64prefix(ifp, nat64prefixes)) != 0) {
111 return error;
112 }
d9a64523
A
113
114 for (i = 0; i < NAT64_MAX_NUM_PREFIXES; i++) {
0a7de745 115 if (nat64prefixes[i].prefix_len != 0) {
d9a64523 116 break;
0a7de745 117 }
d9a64523
A
118 }
119
0a7de745 120 VERIFY(i < NAT64_MAX_NUM_PREFIXES);
d9a64523
A
121
122 struct in6_addr prefix = nat64prefixes[i].ipv6_prefix;
123 int prefix_len = nat64prefixes[i].prefix_len;
124
125 char *ptrv4 = __DECONST(char *, addrv4);
126 char *ptr = __DECONST(char *, 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
0a7de745 135 return -1;
d9a64523
A
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
0a7de745
A
142 IN_SHARED_ADDRESS_SPACE(ntohl(addrv4->s_addr))) { // 100.64.0.0/10 Shared Address Space
143 return -1;
144 }
d9a64523
A
145 }
146
147 memcpy(ptr, (char *)&prefix, prefix_len);
148
149 switch (prefix_len) {
0a7de745
A
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\n", prefix_len);
d9a64523
A
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
0a7de745 181 return error;
d9a64523
A
182}
183
184/* Synthesize ipv4 from ipv6 */
185int
186nat464_synthesize_ipv4(ifnet_t ifp, const struct in6_addr *addr, struct in_addr *addrv4)
187{
188 struct ipv6_prefix nat64prefixes[NAT64_MAX_NUM_PREFIXES];
189 int error = 0, i = 0;
190
191 /* Below call is not optimized as it creates a copy of prefixes */
0a7de745 192 if ((error = ifnet_get_nat64prefix(ifp, nat64prefixes)) != 0) {
d9a64523 193 return error;
0a7de745 194 }
d9a64523
A
195
196 for (i = 0; i < NAT64_MAX_NUM_PREFIXES; i++) {
0a7de745 197 if (nat64prefixes[i].prefix_len != 0) {
d9a64523 198 break;
0a7de745 199 }
d9a64523
A
200 }
201
0a7de745 202 VERIFY(i < NAT64_MAX_NUM_PREFIXES);
d9a64523
A
203
204 struct in6_addr prefix = nat64prefixes[i].ipv6_prefix;
205 int prefix_len = nat64prefixes[i].prefix_len;
206
207 char *ptrv4 = __DECONST(void *, addrv4);
208 char *ptr = __DECONST(void *, addr);
209
0a7de745
A
210 if (memcmp(addr, &prefix, prefix_len) != 0) {
211 return -1;
212 }
d9a64523
A
213
214 switch (prefix_len) {
0a7de745
A
215 case NAT64_PREFIX_LEN_96:
216 memcpy(ptrv4, ptr + 12, 4);
217 break;
218 case NAT64_PREFIX_LEN_64:
219 memcpy(ptrv4, ptr + 9, 4);
220 break;
221 case NAT64_PREFIX_LEN_56:
222 memcpy(ptrv4, ptr + 7, 1);
223 memcpy(ptrv4 + 1, ptr + 9, 3);
224 break;
225 case NAT64_PREFIX_LEN_48:
226 memcpy(ptrv4, ptr + 6, 2);
227 memcpy(ptrv4 + 2, ptr + 9, 2);
228 break;
229 case NAT64_PREFIX_LEN_40:
230 memcpy(ptrv4, ptr + 5, 3);
231 memcpy(ptrv4 + 3, ptr + 9, 1);
232 break;
233 case NAT64_PREFIX_LEN_32:
234 memcpy(ptrv4, ptr + 4, 4);
235 break;
236 default:
237 panic("NAT64-prefix len is wrong: %u\n",
238 prefix_len);
d9a64523
A
239 }
240
0a7de745 241 if (clat_debug) {
d9a64523
A
242 char buf[MAX_IPv4_STR_LEN];
243 clat_log2((LOG_DEBUG, "%s desynthesized to %s\n", __func__,
244 inet_ntop(AF_INET, (void *)addrv4, buf, sizeof(buf))));
245 }
0a7de745 246 return error;
d9a64523
A
247}
248
0a7de745
A
249#define PTR_IP(field) ((int32_t)offsetof(struct ip, field))
250#define PTR_IP6(field) ((int32_t)offsetof(struct ip6_hdr, field))
d9a64523
A
251
252/*
0a7de745
A
253 * Translate the ICMP header
254 */
d9a64523
A
255int
256nat464_translate_icmp(int naf, void *arg)
257{
0a7de745
A
258 struct icmp *icmp4;
259 struct icmp6_hdr *icmp6;
260 uint32_t mtu;
261 int32_t ptr = -1;
262 uint8_t type;
263 uint8_t code;
d9a64523
A
264
265 switch (naf) {
266 case AF_INET:
267 icmp6 = arg;
268 type = icmp6->icmp6_type;
269 code = icmp6->icmp6_code;
270 mtu = ntohl(icmp6->icmp6_mtu);
271
272 switch (type) {
273 case ICMP6_ECHO_REQUEST:
274 type = ICMP_ECHO;
275 break;
276 case ICMP6_ECHO_REPLY:
277 type = ICMP_ECHOREPLY;
278 break;
279 case ICMP6_DST_UNREACH:
280 type = ICMP_UNREACH;
281 switch (code) {
282 case ICMP6_DST_UNREACH_NOROUTE:
283 case ICMP6_DST_UNREACH_BEYONDSCOPE:
284 case ICMP6_DST_UNREACH_ADDR:
285 code = ICMP_UNREACH_HOST;
286 break;
287 case ICMP6_DST_UNREACH_ADMIN:
288 code = ICMP_UNREACH_HOST_PROHIB;
289 break;
290 case ICMP6_DST_UNREACH_NOPORT:
291 code = ICMP_UNREACH_PORT;
292 break;
293 default:
0a7de745 294 return -1;
d9a64523
A
295 }
296 break;
297 case ICMP6_PACKET_TOO_BIG:
298 type = ICMP_UNREACH;
299 code = ICMP_UNREACH_NEEDFRAG;
300 mtu -= 20;
301 break;
302 case ICMP6_TIME_EXCEEDED:
303 type = ICMP_TIMXCEED;
304 break;
305 case ICMP6_PARAM_PROB:
306 switch (code) {
307 case ICMP6_PARAMPROB_HEADER:
308 type = ICMP_PARAMPROB;
309 code = ICMP_PARAMPROB_ERRATPTR;
310 ptr = ntohl(icmp6->icmp6_pptr);
311
0a7de745 312 if (ptr == PTR_IP6(ip6_vfc)) {
d9a64523 313 ; /* preserve */
0a7de745 314 } else if (ptr == PTR_IP6(ip6_vfc) + 1) {
d9a64523 315 ptr = PTR_IP(ip_tos);
0a7de745
A
316 } else if (ptr == PTR_IP6(ip6_plen) ||
317 ptr == PTR_IP6(ip6_plen) + 1) {
d9a64523 318 ptr = PTR_IP(ip_len);
0a7de745 319 } else if (ptr == PTR_IP6(ip6_nxt)) {
d9a64523 320 ptr = PTR_IP(ip_p);
0a7de745 321 } else if (ptr == PTR_IP6(ip6_hlim)) {
d9a64523 322 ptr = PTR_IP(ip_ttl);
0a7de745
A
323 } else if (ptr >= PTR_IP6(ip6_src) &&
324 ptr < PTR_IP6(ip6_dst)) {
d9a64523 325 ptr = PTR_IP(ip_src);
0a7de745
A
326 } else if (ptr >= PTR_IP6(ip6_dst) &&
327 ptr < (int32_t)sizeof(struct ip6_hdr)) {
d9a64523 328 ptr = PTR_IP(ip_dst);
0a7de745
A
329 } else {
330 return -1;
d9a64523
A
331 }
332 break;
333 case ICMP6_PARAMPROB_NEXTHEADER:
334 type = ICMP_UNREACH;
335 code = ICMP_UNREACH_PROTOCOL;
336 break;
337 default:
0a7de745 338 return -1;
d9a64523
A
339 }
340 break;
341 default:
0a7de745 342 return -1;
d9a64523
A
343 }
344 icmp6->icmp6_type = type;
345 icmp6->icmp6_code = code;
346 /* aligns well with a icmpv4 nextmtu */
347 icmp6->icmp6_mtu = htonl(mtu);
348 /* icmpv4 pptr is a one most significant byte */
0a7de745 349 if (ptr >= 0) {
d9a64523 350 icmp6->icmp6_pptr = htonl(ptr << 24);
0a7de745 351 }
d9a64523
A
352 break;
353
354 case AF_INET6:
355 icmp4 = arg;
356 type = icmp4->icmp_type;
357 code = icmp4->icmp_code;
358 mtu = ntohs(icmp4->icmp_nextmtu);
359
360 switch (type) {
361 case ICMP_ECHO:
362 type = ICMP6_ECHO_REQUEST;
363 break;
364 case ICMP_ECHOREPLY:
365 type = ICMP6_ECHO_REPLY;
366 break;
367 case ICMP_UNREACH:
368 type = ICMP6_DST_UNREACH;
369 switch (code) {
370 case ICMP_UNREACH_NET:
371 case ICMP_UNREACH_HOST:
372 case ICMP_UNREACH_NET_UNKNOWN:
373 case ICMP_UNREACH_HOST_UNKNOWN:
374 case ICMP_UNREACH_ISOLATED:
375 case ICMP_UNREACH_TOSNET:
376 case ICMP_UNREACH_TOSHOST:
377 code = ICMP6_DST_UNREACH_NOROUTE;
378 break;
379 case ICMP_UNREACH_PORT:
380 code = ICMP6_DST_UNREACH_NOPORT;
381 break;
382 case ICMP_UNREACH_NET_PROHIB:
383 case ICMP_UNREACH_HOST_PROHIB:
384 case ICMP_UNREACH_FILTER_PROHIB:
385 case ICMP_UNREACH_PRECEDENCE_CUTOFF:
386 code = ICMP6_DST_UNREACH_ADMIN;
387 break;
388 case ICMP_UNREACH_PROTOCOL:
389 type = ICMP6_PARAM_PROB;
390 code = ICMP6_PARAMPROB_NEXTHEADER;
391 ptr = offsetof(struct ip6_hdr, ip6_nxt);
392 break;
393 case ICMP_UNREACH_NEEDFRAG:
394 type = ICMP6_PACKET_TOO_BIG;
395 code = 0;
f427ee49
A
396 /*
397 * Make sure we don't overflow adjusting for
398 * translation overhead.
399 * If we do, just work with a lower mtu as is.
400 */
401 if (mtu <= (UINT16_MAX - CLAT46_HDR_EXPANSION_OVERHD)) {
402 mtu += CLAT46_HDR_EXPANSION_OVERHD;
403 }
d9a64523
A
404 break;
405 default:
0a7de745 406 return -1;
d9a64523
A
407 }
408 break;
409 case ICMP_TIMXCEED:
410 type = ICMP6_TIME_EXCEEDED;
411 break;
412 case ICMP_PARAMPROB:
413 type = ICMP6_PARAM_PROB;
414 switch (code) {
415 case ICMP_PARAMPROB_ERRATPTR:
416 code = ICMP6_PARAMPROB_HEADER;
417 break;
418 case ICMP_PARAMPROB_LENGTH:
419 code = ICMP6_PARAMPROB_HEADER;
420 break;
421 default:
0a7de745 422 return -1;
d9a64523
A
423 }
424
425 ptr = icmp4->icmp_pptr;
0a7de745 426 if (ptr == 0 || ptr == PTR_IP(ip_tos)) {
d9a64523 427 ; /* preserve */
0a7de745
A
428 } else if (ptr == PTR_IP(ip_len) ||
429 ptr == PTR_IP(ip_len) + 1) {
d9a64523 430 ptr = PTR_IP6(ip6_plen);
0a7de745 431 } else if (ptr == PTR_IP(ip_ttl)) {
d9a64523 432 ptr = PTR_IP6(ip6_hlim);
0a7de745 433 } else if (ptr == PTR_IP(ip_p)) {
d9a64523 434 ptr = PTR_IP6(ip6_nxt);
0a7de745
A
435 } else if (ptr >= PTR_IP(ip_src) &&
436 ptr < PTR_IP(ip_dst)) {
d9a64523 437 ptr = PTR_IP6(ip6_src);
0a7de745
A
438 } else if (ptr >= PTR_IP(ip_dst) &&
439 ptr < (int32_t)sizeof(struct ip)) {
d9a64523 440 ptr = PTR_IP6(ip6_dst);
0a7de745
A
441 } else {
442 return -1;
d9a64523
A
443 }
444 break;
445 default:
0a7de745 446 return -1;
d9a64523
A
447 }
448 icmp4->icmp_type = type;
449 icmp4->icmp_code = code;
f427ee49
A
450 icmp4->icmp_nextmtu = htons((uint16_t)mtu);
451
0a7de745 452 if (ptr >= 0) {
d9a64523 453 icmp4->icmp_void = htonl(ptr);
0a7de745 454 }
d9a64523
A
455 break;
456 }
457
0a7de745 458 return 0;
d9a64523
A
459}
460
461/*
462 * @brief This routine is called to perform address family translation on the
463 * inner IP header (that may come as payload) of an ICMP(v4/v6) error
464 * response.
465 *
466 * @param pbuf Pointer to packet buffer
467 * @param off Points to end of ICMP header
468 * @param tot_len Pointer to total length of the outer IP header
469 * @param off2 Points to end of inner IP header
470 * @param proto2 Inner IP proto field
471 * @param ttl2 Inner IP ttl field
472 * @param tot_len2 Inner IP total length
473 * @param src Pointer to the generic v4/v6 src address
474 * @param dst Pointer to the generic v4/v6 dst address
475 * @param af Old protocol family
476 * @param naf New protocol family
477 *
478 * @return -1 on error and 0 on success
479 */
480int
f427ee49
A
481nat464_translate_icmp_ip(pbuf_t *pbuf, uint16_t off, uint16_t *tot_len, uint16_t *off2,
482 uint8_t proto2, uint8_t ttl2, uint16_t tot_len2, struct nat464_addr *src,
0a7de745 483 struct nat464_addr *dst, protocol_family_t af, protocol_family_t naf)
d9a64523
A
484{
485 struct ip *ip4 = NULL;
486 struct ip6_hdr *ip6 = NULL;
487 void *hdr = NULL;
488 int hlen = 0, olen = 0;
489
490 if (af == naf || (af != AF_INET && af != AF_INET6) ||
0a7de745
A
491 (naf != AF_INET && naf != AF_INET6)) {
492 return -1;
493 }
d9a64523
A
494
495 /* old header */
496 olen = *off2 - off;
497 /* new header */
498 hlen = naf == PF_INET ? sizeof(*ip4) : sizeof(*ip6);
499
500 /* Modify the pbuf to accommodate the new header */
501 hdr = pbuf_resize_segment(pbuf, off, olen, hlen);
0a7de745
A
502 if (hdr == NULL) {
503 return -1;
504 }
d9a64523
A
505
506 /* translate inner ip/ip6 header */
507 switch (naf) {
508 case AF_INET:
509 ip4 = hdr;
510 bzero(ip4, sizeof(*ip4));
511 ip4->ip_v = IPVERSION;
512 ip4->ip_hl = sizeof(*ip4) >> 2;
f427ee49 513 ip4->ip_len = htons((uint16_t)(sizeof(*ip4) + tot_len2 - olen));
d9a64523
A
514 ip4->ip_id = rfc6864 ? 0 : htons(ip_randomid());
515 ip4->ip_off = htons(IP_DF);
516 ip4->ip_ttl = ttl2;
0a7de745 517 if (proto2 == IPPROTO_ICMPV6) {
d9a64523 518 ip4->ip_p = IPPROTO_ICMP;
0a7de745 519 } else {
d9a64523 520 ip4->ip_p = proto2;
0a7de745 521 }
d9a64523
A
522 ip4->ip_src = src->natv4addr;
523 ip4->ip_dst = dst->natv4addr;
524 ip4->ip_sum = pbuf_inet_cksum(pbuf, 0, 0, ip4->ip_hl << 2);
525
526 if (clat_debug) {
527 char buf[MAX_IPv4_STR_LEN];
528 clat_log2((LOG_DEBUG, "%s translated to IPv4 (inner) "
529 "ip_len: %#x ip_p: %d ip_sum: %#x ip_src: %s ip_dst: %s \n",
530 __func__, ntohs(ip4->ip_len), ip4->ip_p, ntohs(ip4->ip_sum),
531 inet_ntop(AF_INET, (void *)&ip4->ip_src, buf, sizeof(buf)),
532 inet_ntop(AF_INET, (void *)&ip4->ip_dst, buf, sizeof(buf))));
533 }
534 break;
535 case AF_INET6:
536 ip6 = hdr;
537 bzero(ip6, sizeof(*ip6));
538 ip6->ip6_vfc = IPV6_VERSION;
f427ee49 539 ip6->ip6_plen = htons((uint16_t)(tot_len2 - olen));
0a7de745 540 if (proto2 == IPPROTO_ICMP) {
d9a64523 541 ip6->ip6_nxt = IPPROTO_ICMPV6;
0a7de745 542 } else {
d9a64523 543 ip6->ip6_nxt = proto2;
0a7de745
A
544 }
545 if (!ttl2 || ttl2 > IPV6_DEFHLIM) {
d9a64523 546 ip6->ip6_hlim = IPV6_DEFHLIM;
0a7de745 547 } else {
d9a64523 548 ip6->ip6_hlim = ttl2;
0a7de745 549 }
d9a64523
A
550 ip6->ip6_src = src->natv6addr;
551 ip6->ip6_dst = dst->natv6addr;
552
553 if (clat_debug) {
554 char buf2[MAX_IPv6_STR_LEN];
555 clat_log2((LOG_DEBUG, "%s translated to IPv6 (inner) "
556 "ip6_plen: %#x ip6_nxt: %d ip6_src: %s ip6_dst: %s \n",
557 __func__, ntohs(ip6->ip6_plen), ip6->ip6_nxt,
558 inet_ntop(AF_INET6, (void *)&ip6->ip6_src, buf2, sizeof(buf2)),
559 inet_ntop(AF_INET6, (void *)&ip6->ip6_dst, buf2, sizeof(buf2))));
560 }
561 break;
562 }
563
564 /* adjust payload offset and total packet length */
565 *off2 += hlen - olen;
566 *tot_len += hlen - olen;
567
0a7de745 568 return 0;
d9a64523
A
569}
570/*
571 * @brief The function inserts IPv6 fragmentation header
572 * and populates it with the passed parameters.
573 *
574 * @param pbuf Pointer to the packet buffer
575 * @param ip_id IP identifier (in network byte order)
576 * @param frag_offset Fragment offset (in network byte order)
577 * @param is_last_frag Boolean indicating if the fragment header is for
578 * last fragment or not.
579 *
580 * @return -1 on error and 0 on success.
581 */
582int
583nat464_insert_frag46(pbuf_t *pbuf, uint16_t ip_id_val, uint16_t frag_offset,
584 boolean_t is_last_frag)
585{
586 struct ip6_frag *p_ip6_frag = NULL;
587 struct ip6_hdr *p_ip6h = NULL;
588
589 /* Insert IPv6 fragmentation header */
590 if (pbuf_resize_segment(pbuf, sizeof(struct ip6_hdr), 0,
0a7de745
A
591 sizeof(struct ip6_frag)) == NULL) {
592 return -1;
593 }
d9a64523
A
594
595 p_ip6h = mtod(pbuf->pb_mbuf, struct ip6_hdr *);
596 p_ip6_frag = (struct ip6_frag *)pbuf_contig_segment(pbuf,
597 sizeof(struct ip6_hdr), sizeof(struct ip6_frag));
598
0a7de745
A
599 if (p_ip6_frag == NULL) {
600 return -1;
601 }
d9a64523
A
602
603 /* Populate IPv6 fragmentation header */
604 p_ip6_frag->ip6f_nxt = p_ip6h->ip6_nxt;
605 p_ip6_frag->ip6f_reserved = 0;
f427ee49 606 p_ip6_frag->ip6f_offlg = (uint16_t)(frag_offset << 3);
0a7de745 607 if (!is_last_frag) {
d9a64523 608 p_ip6_frag->ip6f_offlg |= 0x1;
0a7de745 609 }
d9a64523
A
610 p_ip6_frag->ip6f_offlg = htons(p_ip6_frag->ip6f_offlg);
611 p_ip6_frag->ip6f_ident = ip_id_val;
612
613 /* Update IPv6 header */
614 p_ip6h->ip6_nxt = IPPROTO_FRAGMENT;
615 p_ip6h->ip6_plen = htons(ntohs(p_ip6h->ip6_plen) +
616 sizeof(struct ip6_frag));
617
0a7de745 618 return 0;
d9a64523
A
619}
620
621int
622nat464_translate_64(pbuf_t *pbuf, int off, uint8_t tos,
623 uint8_t *proto, uint8_t ttl, struct in_addr src_v4,
624 struct in_addr dst_v4, uint64_t tot_len, boolean_t *p_is_first_frag)
625{
626 struct ip *ip4;
627 struct ip6_frag *p_frag6 = NULL;
628 struct ip6_frag frag6 = {};
629 boolean_t is_frag = FALSE;
630 uint16_t ip_frag_off = 0;
631
632 /*
633 * ip_input asserts for rcvif to be not NULL
634 * That may not be true for two corner cases
635 * 1. If for some reason a local app sends DNS
636 * AAAA query to local host
637 * 2. If IPv6 stack in kernel internally generates a
638 * message destined for a synthesized IPv6 end-point.
639 */
0a7de745
A
640 if (pbuf->pb_ifp == NULL) {
641 return NT_DROP;
642 }
d9a64523
A
643
644 if (*proto == IPPROTO_FRAGMENT) {
645 p_frag6 = (struct ip6_frag *)pbuf_contig_segment(pbuf,
646 sizeof(struct ip6_hdr), sizeof(struct ip6_frag));
647 if (p_frag6 == NULL) {
648 ip6stat.ip6s_clat464_in_64frag_transfail_drop++;
0a7de745 649 return NT_DROP;
d9a64523
A
650 }
651
652 frag6 = *p_frag6;
653 p_frag6 = NULL;
654 *proto = frag6.ip6f_nxt;
655 off += sizeof(struct ip6_frag);
656 is_frag = TRUE;
657 ip_frag_off = (ntohs(frag6.ip6f_offlg & IP6F_OFF_MASK)) >> 3;
658 if (ip_frag_off != 0) {
659 *p_is_first_frag = FALSE;
660 }
661 }
662
663 ip4 = (struct ip *)pbuf_resize_segment(pbuf, 0, off, sizeof(*ip4));
0a7de745
A
664 if (ip4 == NULL) {
665 return NT_DROP;
666 }
d9a64523
A
667 ip4->ip_v = 4;
668 ip4->ip_hl = 5;
669 ip4->ip_tos = tos;
f427ee49 670 ip4->ip_len = htons((uint16_t)(sizeof(*ip4) + (tot_len - off)));
d9a64523
A
671 ip4->ip_id = 0;
672 ip4->ip_off = 0;
673 ip4->ip_ttl = ttl;
674 ip4->ip_p = *proto;
675 ip4->ip_sum = 0;
676 ip4->ip_src = src_v4;
677 ip4->ip_dst = dst_v4;
678 if (is_frag) {
679 /*
680 * https://tools.ietf.org/html/rfc7915#section-5.1.1
681 * Identification: Copied from the low-order 16 bits in the
682 * Identification field in the Fragment Header.
683 */
684 ip4->ip_id = ntohl(frag6.ip6f_ident) & 0xffff;
685 ip4->ip_id = htons(ip4->ip_id);
0a7de745 686 if (frag6.ip6f_offlg & IP6F_MORE_FRAG) {
d9a64523 687 ip_frag_off |= IP_MF;
0a7de745 688 }
d9a64523
A
689 ip4->ip_off = htons(ip_frag_off);
690 } else {
691 ip4->ip_off |= htons(IP_DF);
692 }
693
694 /*
695 * Defer calculating ip_sum for ICMPv6 as we do it
696 * later in Protocol translation
697 */
0a7de745 698 if (*proto != IPPROTO_ICMPV6) {
d9a64523 699 ip4->ip_sum = pbuf_inet_cksum(pbuf, 0, 0, ip4->ip_hl << 2);
0a7de745 700 }
d9a64523
A
701
702 if (clat_debug) {
703 char buf1[MAX_IPv4_STR_LEN], buf2[MAX_IPv4_STR_LEN];
704 clat_log2((LOG_DEBUG, "%s translated to IPv4 ip_len: %#x "
705 "ip_p: %d ip_sum: %#x ip_src: %s ip_dst: %s \n", __func__,
706 ntohs(ip4->ip_len), ip4->ip_p, ntohs(ip4->ip_sum),
707 inet_ntop(AF_INET, (void *)&ip4->ip_src, buf1, sizeof(buf1)),
708 inet_ntop(AF_INET, (void *)&ip4->ip_dst, buf2, sizeof(buf2))));
709 }
0a7de745 710 return NT_NAT64;
d9a64523
A
711}
712/*
713 * @brief The routine translates the IPv4 header to IPv6 header.
714 *
715 * @param pbuf Pointer to the generic packet buffer
716 * @param off Offset to the end of IP header
717 * @param tos Type of service
718 * @param proto Protocol running over IP
719 * @param ttl Time to live
720 * @param src_v6 Source IPv6 address
721 * @param dst_v6 Destination IPv6 address
722 * @param tot_len Total payload length
723 *
724 * @return NT_NAT64 if IP header translation is successful, else error
0a7de745 725 */
d9a64523 726int
f427ee49 727nat464_translate_46(pbuf_t *pbuf, uint16_t off, uint8_t tos,
d9a64523 728 uint8_t proto, uint8_t ttl, struct in6_addr src_v6,
f427ee49 729 struct in6_addr dst_v6, uint16_t tot_len)
d9a64523
A
730{
731 struct ip6_hdr *ip6;
732
0a7de745
A
733 if (pbuf->pb_ifp == NULL) {
734 return NT_DROP;
735 }
d9a64523
A
736
737 /*
738 * Trim the buffer from head of size equal to to off (which is equal to
739 * the size of IP header and prepend IPv6 header length to the buffer
0a7de745 740 */
d9a64523 741 ip6 = (struct ip6_hdr *)pbuf_resize_segment(pbuf, 0, off, sizeof(*ip6));
0a7de745
A
742 if (ip6 == NULL) {
743 return NT_DROP;
744 }
d9a64523
A
745 ip6->ip6_flow = htonl((6 << 28) | (tos << 20));
746 ip6->ip6_plen = htons(tot_len - off);
747 ip6->ip6_nxt = proto;
748 ip6->ip6_hlim = ttl;
749 ip6->ip6_src = src_v6;
750 ip6->ip6_dst = dst_v6;
751
752 if (clat_debug) {
753 char buf1[MAX_IPv6_STR_LEN], buf2[MAX_IPv6_STR_LEN];
754 clat_log2((LOG_DEBUG, "%s translated to IPv6 ip6_plen: %#x "
755 " ip6_nxt: %d ip6_src: %s ip6_dst: %s \n", __func__,
756 ntohs(ip6->ip6_plen), ip6->ip6_nxt,
757 inet_ntop(AF_INET6, (void *)&ip6->ip6_src, buf1, sizeof(buf1)),
758 inet_ntop(AF_INET6, (void *)&ip6->ip6_dst, buf2, sizeof(buf2))));
759 }
0a7de745 760 return NT_NAT64;
d9a64523
A
761}
762
763/* Handle the next protocol checksum */
764/*
765 * @brief This routine translates the Proto running over IP and updates the checksum
766 * for IP header translation. It also updates pbuf checksum flags and related fields.
767 *
768 * @param pbuf Pointer to protocol buffer
769 * @param nsrc New source address
770 * @param ndst New destination address
771 * @param af Old family
772 * @param naf New family
773 *
774 * @return void
0a7de745 775 */
d9a64523
A
776int
777nat464_translate_proto(pbuf_t *pbuf, struct nat464_addr *osrc,
778 struct nat464_addr *odst, uint8_t oproto, protocol_family_t af,
779 protocol_family_t naf, int direction, boolean_t only_csum)
780{
781 struct ip *iph = NULL;
782 struct ip6_hdr *ip6h = NULL;
f427ee49
A
783 uint16_t hlen = 0, plen = 0;
784 uint16_t tot_len = 0;
d9a64523
A
785 void *nsrc = NULL, *ndst = NULL;
786 uint8_t *proto = 0;
787 uint16_t *psum = NULL;
788 boolean_t do_ones_complement = FALSE;
789
790 /* For now these routines only support 464 translations */
791 VERIFY(af != naf);
792 VERIFY(af == PF_INET || af == PF_INET6);
793
794 /*
795 * For now out must be for v4 to v6 translation
796 * and in must be for v6 to v4 translation.
797 */
798 switch (naf) {
799 case PF_INET: {
800 iph = pbuf->pb_data;
f427ee49 801 hlen = (uint16_t)(iph->ip_hl << 2);
d9a64523
A
802 plen = ntohs(iph->ip_len) - hlen;
803 tot_len = ntohs(iph->ip_len);
804 nsrc = &iph->ip_src;
805 ndst = &iph->ip_dst;
806 proto = &iph->ip_p;
807 break;
808 }
809 case PF_INET6: {
810 ip6h = pbuf->pb_data;
f427ee49 811 hlen = (uint16_t)sizeof(*ip6h);
d9a64523
A
812 plen = ntohs(ip6h->ip6_plen);
813 tot_len = hlen + plen;
814 nsrc = &ip6h->ip6_src;
815 ndst = &ip6h->ip6_dst;
816 proto = &ip6h->ip6_nxt;
817 break;
818 }
cb323159
A
819 default:
820 return NT_DROP; /* We should never come here */
d9a64523
A
821 }
822
cb323159
A
823 if (*proto != oproto) {
824 return NT_DROP;
825 }
d9a64523
A
826
827 /*
828 * We may want to manipulate csum flags in some cases
829 * and not act on the protocol header as it may not
830 * carry protocol checksums.
831 * For example, fragments other than the first one would
832 * not carry protocol headers.
833 */
834 if (only_csum) {
835 /*
836 * Only translate ICMP proto in the header
837 * and adjust checksums
838 */
839 if (*proto == IPPROTO_ICMP) {
0a7de745
A
840 if (naf != PF_INET6) {
841 return NT_DROP;
842 }
d9a64523
A
843
844 *proto = IPPROTO_ICMPV6;
0a7de745
A
845 } else if (*proto == IPPROTO_ICMPV6) {
846 if (naf != PF_INET) {
847 return NT_DROP;
848 }
d9a64523
A
849
850 *proto = IPPROTO_ICMP;
851 /* Recalculate IP checksum as proto field has changed */
852 iph->ip_sum = 0;
853 iph->ip_sum = pbuf_inet_cksum(pbuf, 0, 0, hlen);
854 }
855 goto done;
856 }
857
858 switch (*proto) {
859 case IPPROTO_UDP: {
860 struct udphdr *uh = (struct udphdr *)pbuf_contig_segment(pbuf, hlen,
861 sizeof(*uh));
862
0a7de745
A
863 if (uh == NULL) {
864 return NT_DROP;
865 }
d9a64523
A
866
867 if (!(*pbuf->pb_csum_flags & (CSUM_UDP | CSUM_PARTIAL)) &&
868 uh->uh_sum == 0 && af == PF_INET && naf == PF_INET6) {
869 uh->uh_sum = pbuf_inet6_cksum(pbuf, IPPROTO_UDP,
870 hlen, ntohs(ip6h->ip6_plen));
0a7de745 871 if (uh->uh_sum == 0) {
d9a64523 872 uh->uh_sum = 0xffff;
0a7de745 873 }
d9a64523
A
874 goto done;
875 }
876
877 psum = &uh->uh_sum;
878 break;
879 }
880 case IPPROTO_TCP: {
881 struct tcphdr *th = (struct tcphdr *)pbuf_contig_segment(pbuf, hlen,
882 sizeof(*th));
883
0a7de745
A
884 if (th == NULL) {
885 return NT_DROP;
886 }
d9a64523
A
887
888 psum = &th->th_sum;
889 break;
890 }
891 }
892
0a7de745 893 /*
d9a64523
A
894 * Translate the protocol header, update IP header if needed,
895 * calculate checksums and update the checksum flags.
896 */
897 switch (*proto) {
898 case IPPROTO_UDP:
0a7de745 899 /* Fall through */
d9a64523
A
900 case IPPROTO_TCP:
901 {
902 /*
903 * If it is a locally generated and has CSUM flags set
904 * for TCP and UDP it means we have pseudo header checksum
905 * that has not yet been one's complemented.
906 */
907 if (direction == NT_OUT &&
0a7de745 908 (*pbuf->pb_csum_flags & CSUM_DELAY_DATA)) {
d9a64523 909 do_ones_complement = TRUE;
0a7de745 910 }
d9a64523
A
911
912 nat464_addr_cksum_fixup(psum, osrc, (struct nat464_addr *)nsrc,
913 af, naf, (*proto == IPPROTO_UDP) ? 1 : 0, do_ones_complement);
914 nat464_addr_cksum_fixup(psum, odst, (struct nat464_addr *)ndst,
915 af, naf, (*proto == IPPROTO_UDP) ? 1 : 0, do_ones_complement);
916
917 break;
918 }
919 case IPPROTO_ICMP: {
0a7de745
A
920 if (naf != PF_INET6) { /* allow only v6 as naf for ICMP */
921 return NT_DROP;
922 }
d9a64523
A
923
924 struct icmp *icmph = NULL;
925 struct icmp6_hdr *icmp6h = NULL;
f427ee49 926 uint16_t ip2off = 0, hlen2 = 0, tot_len2 = 0;
d9a64523
A
927
928 icmph = (struct icmp*) pbuf_contig_segment(pbuf, hlen,
929 ICMP_MINLEN);
0a7de745
A
930 if (icmph == NULL) {
931 return NT_DROP;
932 }
d9a64523
A
933
934 /* Translate the ICMP header */
0a7de745
A
935 if (nat464_translate_icmp(PF_INET6, icmph) != 0) {
936 return NT_DROP;
937 }
d9a64523
A
938
939 *proto = IPPROTO_ICMPV6;
940 icmp6h = (struct icmp6_hdr *)(uintptr_t)icmph;
941 pbuf_copy_back(pbuf, hlen, sizeof(struct icmp6_hdr),
942 icmp6h);
943
944 /*Translate the inner IP header only for error messages */
945 if (ICMP6_ERRORTYPE(icmp6h->icmp6_type)) {
f427ee49
A
946 ip2off = (uint16_t)(hlen + sizeof(*icmp6h));
947 struct ip *iph2 = NULL;
d9a64523 948 iph2 = (struct ip*) pbuf_contig_segment(pbuf, ip2off,
0a7de745
A
949 sizeof(*iph2));
950 if (iph2 == NULL) {
951 return NT_DROP;
952 }
d9a64523 953
f427ee49 954 hlen2 = (uint16_t)(ip2off + (iph2->ip_hl << 2));
d9a64523
A
955 tot_len2 = ntohs(iph2->ip_len);
956
957 /* Destination in outer IP should be Source in inner IP */
958 VERIFY(IN_ARE_ADDR_EQUAL(&odst->natv4addr, &iph2->ip_src));
959 if (nat464_translate_icmp_ip(pbuf, ip2off, &tot_len,
960 &hlen2, iph2->ip_p, iph2->ip_ttl, tot_len2,
961 (struct nat464_addr *)ndst, (struct nat464_addr *)nsrc,
0a7de745
A
962 PF_INET, PF_INET6) != 0) {
963 return NT_DROP;
964 }
d9a64523
A
965 /* Update total length/payload length for outer header */
966 switch (naf) {
967 case PF_INET:
0a7de745 968 iph->ip_len = htons(tot_len);
d9a64523
A
969 break;
970 case PF_INET6:
0a7de745 971 ip6h->ip6_plen = htons(tot_len - hlen);
d9a64523
A
972 break;
973 }
974 iph2 = NULL;
975 }
976
977 icmp6h->icmp6_cksum = 0;
978 icmp6h->icmp6_cksum = pbuf_inet6_cksum(pbuf, IPPROTO_ICMPV6, hlen,
979 ntohs(ip6h->ip6_plen));
980
981 clat_log2((LOG_DEBUG, "%s translated to ICMPV6 type: %d "
982 "code: %d checksum: %#x \n", __func__, icmp6h->icmp6_type,
983 icmp6h->icmp6_code, icmp6h->icmp6_cksum));
984
985 icmph = NULL;
986 icmp6h = NULL;
987 break;
0a7de745 988 }
d9a64523 989 case IPPROTO_ICMPV6:
0a7de745
A
990 { if (naf != PF_INET) { /* allow only v4 as naf for ICMPV6 */
991 return NT_DROP;
992 }
d9a64523
A
993
994 struct icmp6_hdr *icmp6h = NULL;
995 struct icmp *icmph = NULL;
f427ee49 996 uint16_t ip2off = 0, hlen2 = 0, tot_len2 = 0;
d9a64523
A
997
998 icmp6h = (struct icmp6_hdr*) pbuf_contig_segment(pbuf, hlen,
999 sizeof(*icmp6h));
0a7de745
A
1000 if (icmp6h == NULL) {
1001 return NT_DROP;
1002 }
d9a64523
A
1003
1004 /* Translate the ICMP header */
0a7de745
A
1005 if (nat464_translate_icmp(PF_INET, icmp6h) != 0) {
1006 return NT_DROP;
1007 }
d9a64523
A
1008
1009 *proto = IPPROTO_ICMP;
1010 icmph = (struct icmp *)(uintptr_t)icmp6h;
1011 pbuf_copy_back(pbuf, hlen, ICMP_MINLEN,
1012 icmph);
1013
1014 /*Translate the inner IP header only for error messages */
1015 if (ICMP_ERRORTYPE(icmph->icmp_type)) {
1016 ip2off = hlen + ICMP_MINLEN;
f427ee49 1017 struct ip6_hdr *iph2 = NULL;
d9a64523 1018 iph2 = (struct ip6_hdr*) pbuf_contig_segment(pbuf, ip2off,
0a7de745
A
1019 sizeof(*iph2));
1020 if (iph2 == NULL) {
1021 return NT_DROP;
1022 }
d9a64523
A
1023
1024 /* hlen2 points to end of inner IP header from the beginning */
1025 hlen2 = ip2off + sizeof(struct ip6_hdr);
1026 tot_len2 = ntohs(iph2->ip6_plen) + sizeof(struct ip6_hdr);
1027
1028 if (nat464_translate_icmp_ip(pbuf, ip2off, &tot_len,
1029 &hlen2, iph2->ip6_nxt, iph2->ip6_hlim, tot_len2,
1030 (struct nat464_addr *)ndst, (struct nat464_addr *)nsrc,
0a7de745
A
1031 PF_INET6, PF_INET) != 0) {
1032 return NT_DROP;
1033 }
d9a64523
A
1034
1035 /* Update total length for outer header */
1036 switch (naf) {
1037 case PF_INET:
0a7de745 1038 iph->ip_len = htons(tot_len);
d9a64523
A
1039 break;
1040 case PF_INET6:
0a7de745 1041 ip6h->ip6_plen = htons(tot_len - hlen);
d9a64523
A
1042 break;
1043 }
1044 iph2 = NULL;
1045 }
1046 /* Recalculate IP checksum as some IP fields might have changed */
1047 iph->ip_sum = 0;
1048 iph->ip_sum = pbuf_inet_cksum(pbuf, 0, 0, iph->ip_hl << 2);
1049 icmph->icmp_cksum = 0;
1050 icmph->icmp_cksum = pbuf_inet_cksum(pbuf, 0, hlen,
1051 ntohs(iph->ip_len) - hlen);
1052
1053 clat_log2((LOG_DEBUG, "%s translated to ICMP type: %d "
1054 "code: %d checksum: %#x \n", __func__, icmph->icmp_type,
1055 icmph->icmp_code, icmph->icmp_cksum));
1056
1057 icmp6h = NULL;
1058 icmph = NULL;
0a7de745 1059 break;}
d9a64523
A
1060
1061 /*
1062 * https://tools.ietf.org/html/rfc7915#section-5.1.1
1063 * If the Next Header field of the Fragment Header is an
1064 * extension header (except ESP, but including the Authentication
1065 * Header (AH)), then the packet SHOULD be dropped and logged.
1066 */
1067 case IPPROTO_HOPOPTS:
1068 case IPPROTO_ROUTING:
1069 case IPPROTO_DSTOPTS:
1070 case IPPROTO_AH:
0a7de745 1071 return NT_DROP;
d9a64523
A
1072
1073 case IPPROTO_FRAGMENT:
1074 /*
1075 * The fragment header is appended after or removed before
1076 * calling into this routine.
1077 */
1078 VERIFY(FALSE);
1079 case IPPROTO_ESP:
1080 break;
1081
1082 default:
0a7de745 1083 return NT_DROP;
d9a64523
A
1084 }
1085
1086done:
1087 /* Update checksum flags and offsets based on direction */
1088 if (direction == NT_OUT) {
1089 if ((*pbuf->pb_csum_flags & (CSUM_DATA_VALID | CSUM_PARTIAL)) ==
1090 (CSUM_DATA_VALID | CSUM_PARTIAL)) {
1091 (pbuf->pb_mbuf)->m_pkthdr.csum_tx_start += CLAT46_HDR_EXPANSION_OVERHD;
1092 (pbuf->pb_mbuf)->m_pkthdr.csum_tx_stuff += CLAT46_HDR_EXPANSION_OVERHD;
1093 }
1094
0a7de745 1095 if (*pbuf->pb_csum_flags & CSUM_TCP) {
d9a64523 1096 *pbuf->pb_csum_flags |= CSUM_TCPIPV6;
0a7de745
A
1097 }
1098 if (*pbuf->pb_csum_flags & CSUM_UDP) {
d9a64523 1099 *pbuf->pb_csum_flags |= CSUM_UDPIPV6;
0a7de745
A
1100 }
1101 if (*pbuf->pb_csum_flags & CSUM_FRAGMENT) {
d9a64523 1102 *pbuf->pb_csum_flags |= CSUM_FRAGMENT_IPV6;
0a7de745 1103 }
d9a64523
A
1104
1105 /* Clear IPv4 checksum flags */
1106 *pbuf->pb_csum_flags &= ~(CSUM_IP | CSUM_IP_FRAGS | CSUM_DELAY_DATA | CSUM_FRAGMENT);
f427ee49
A
1107 /*
1108 * If the packet requires TCP segmentation due to TSO offload,
1109 * then change the checksum flag to indicate that an IPv6
1110 * TCP segmentation is needed now.
1111 */
1112 if (*pbuf->pb_csum_flags & CSUM_TSO_IPV4) {
1113 *pbuf->pb_csum_flags &= ~CSUM_TSO_IPV4;
1114 *pbuf->pb_csum_flags |= CSUM_TSO_IPV6;
1115 }
d9a64523
A
1116 } else if (direction == NT_IN) {
1117 /* XXX On input just reset csum flags */
1118 *pbuf->pb_csum_flags = 0; /* Reset all flags for now */
1119#if 0
1120 /* Update csum flags and offsets for rx */
1121 if (*pbuf->pb_csum_flags & CSUM_PARTIAL) {
1122 (pbuf->pb_mbuf)->m_pkthdr.csum_rx_start -= CLAT46_HDR_EXPANSION_OVERHD;
1123 }
1124#endif
1125 }
0a7de745 1126 return NT_NAT64;
d9a64523
A
1127}
1128
1129/* Fix the proto checksum for address change */
1130static void
1131nat464_addr_cksum_fixup(uint16_t *pc, struct nat464_addr *ao, struct nat464_addr *an,
1132 protocol_family_t af, protocol_family_t naf, uint8_t u, boolean_t do_ones_complement)
1133{
1134 /* Currently we only support v4 to v6 and vice versa */
0a7de745 1135 VERIFY(af != naf);
d9a64523
A
1136
1137 switch (af) {
1138 case PF_INET:
1139 switch (naf) {
1140 case PF_INET6:
1141 if (do_ones_complement) {
1142 *pc = ~nat464_cksum_fixup(nat464_cksum_fixup(
0a7de745
A
1143 nat464_cksum_fixup(nat464_cksum_fixup(nat464_cksum_fixup(
1144 nat464_cksum_fixup(nat464_cksum_fixup(nat464_cksum_fixup(~*pc,
1145 ao->nataddr16[0], an->nataddr16[0], u),
1146 ao->nataddr16[1], an->nataddr16[1], u),
1147 0, an->nataddr16[2], u),
1148 0, an->nataddr16[3], u),
1149 0, an->nataddr16[4], u),
1150 0, an->nataddr16[5], u),
1151 0, an->nataddr16[6], u),
1152 0, an->nataddr16[7], u);
d9a64523
A
1153 } else {
1154 *pc = nat464_cksum_fixup(nat464_cksum_fixup(
0a7de745
A
1155 nat464_cksum_fixup(nat464_cksum_fixup(nat464_cksum_fixup(
1156 nat464_cksum_fixup(nat464_cksum_fixup(nat464_cksum_fixup(*pc,
1157 ao->nataddr16[0], an->nataddr16[0], u),
1158 ao->nataddr16[1], an->nataddr16[1], u),
1159 0, an->nataddr16[2], u),
1160 0, an->nataddr16[3], u),
1161 0, an->nataddr16[4], u),
1162 0, an->nataddr16[5], u),
1163 0, an->nataddr16[6], u),
1164 0, an->nataddr16[7], u);
d9a64523
A
1165 }
1166 break;
1167 }
1168 break;
1169 case PF_INET6:
1170 /*
1171 * XXX For NAT464 this only applies to the incoming path.
1172 * The checksum therefore is already ones complemented.
1173 * Therefore we just perform normal fixup.
1174 */
1175 switch (naf) {
1176 case PF_INET:
1177 *pc = nat464_cksum_fixup(nat464_cksum_fixup(
0a7de745
A
1178 nat464_cksum_fixup(nat464_cksum_fixup(nat464_cksum_fixup(
1179 nat464_cksum_fixup(nat464_cksum_fixup(nat464_cksum_fixup(*pc,
1180 ao->nataddr16[0], an->nataddr16[0], u),
1181 ao->nataddr16[1], an->nataddr16[1], u),
1182 ao->nataddr16[2], 0, u),
1183 ao->nataddr16[3], 0, u),
1184 ao->nataddr16[4], 0, u),
1185 ao->nataddr16[5], 0, u),
1186 ao->nataddr16[6], 0, u),
1187 ao->nataddr16[7], 0, u);
d9a64523
A
1188 break;
1189 }
1190 break;
1191 }
1192}
1193
1194uint16_t
1195nat464_cksum_fixup(uint16_t cksum, uint16_t old, uint16_t new, uint8_t udp)
1196{
1197 uint32_t l;
1198
0a7de745
A
1199 if (udp && !cksum) {
1200 return 0;
1201 }
d9a64523
A
1202 l = cksum + old - new;
1203 l = (l >> 16) + (l & 0xffff);
1204 l = l & 0xffff;
0a7de745
A
1205 if (udp && !l) {
1206 return 0xffff;
1207 }
f427ee49 1208 return (uint16_t)l;
d9a64523
A
1209}
1210
1211/* CLAT46 event handlers */
1212void
1213in6_clat46_eventhdlr_callback(struct eventhandler_entry_arg arg0 __unused,
1214 in6_clat46_evhdlr_code_t in6_clat46_ev_code, pid_t epid, uuid_t euuid)
1215{
0a7de745
A
1216 struct kev_msg ev_msg;
1217 struct kev_netevent_clat46_data clat46_event_data;
d9a64523 1218
0a7de745
A
1219 bzero(&ev_msg, sizeof(ev_msg));
1220 bzero(&clat46_event_data, sizeof(clat46_event_data));
d9a64523 1221
0a7de745
A
1222 ev_msg.vendor_code = KEV_VENDOR_APPLE;
1223 ev_msg.kev_class = KEV_NETWORK_CLASS;
1224 ev_msg.kev_subclass = KEV_NETEVENT_SUBCLASS;
1225 ev_msg.event_code = KEV_NETEVENT_CLAT46_EVENT;
d9a64523 1226
0a7de745
A
1227 bzero(&clat46_event_data, sizeof(clat46_event_data));
1228 clat46_event_data.clat46_event_code = in6_clat46_ev_code;
1229 clat46_event_data.epid = epid;
1230 uuid_copy(clat46_event_data.euuid, euuid);
d9a64523 1231
0a7de745
A
1232 ev_msg.dv[0].data_ptr = &clat46_event_data;
1233 ev_msg.dv[0].data_length = sizeof(clat46_event_data);
d9a64523 1234
0a7de745 1235 kev_post_msg(&ev_msg);
d9a64523
A
1236}
1237
1238static void
1239in6_clat46_event_callback(void *arg)
1240{
0a7de745
A
1241 struct kev_netevent_clat46_data *p_in6_clat46_ev =
1242 (struct kev_netevent_clat46_data *)arg;
d9a64523 1243
0a7de745
A
1244 EVENTHANDLER_INVOKE(&in6_clat46_evhdlr_ctxt, in6_clat46_event,
1245 p_in6_clat46_ev->clat46_event_code, p_in6_clat46_ev->epid,
1246 p_in6_clat46_ev->euuid);
d9a64523
A
1247}
1248
0a7de745
A
1249struct in6_clat46_event_nwk_wq_entry {
1250 struct nwk_wq_entry nwk_wqe;
1251 struct kev_netevent_clat46_data in6_clat46_ev_arg;
d9a64523
A
1252};
1253
1254void
1255in6_clat46_event_enqueue_nwk_wq_entry(in6_clat46_evhdlr_code_t in6_clat46_event_code,
1256 pid_t epid, uuid_t euuid)
1257{
0a7de745 1258 struct in6_clat46_event_nwk_wq_entry *p_ev = NULL;
d9a64523 1259
0a7de745
A
1260 MALLOC(p_ev, struct in6_clat46_event_nwk_wq_entry *,
1261 sizeof(struct in6_clat46_event_nwk_wq_entry),
1262 M_NWKWQ, M_WAITOK | M_ZERO);
d9a64523 1263
0a7de745
A
1264 p_ev->nwk_wqe.func = in6_clat46_event_callback;
1265 p_ev->nwk_wqe.is_arg_managed = TRUE;
1266 p_ev->nwk_wqe.arg = &p_ev->in6_clat46_ev_arg;
d9a64523 1267
0a7de745
A
1268 p_ev->in6_clat46_ev_arg.clat46_event_code = in6_clat46_event_code;
1269 p_ev->in6_clat46_ev_arg.epid = epid;
1270 uuid_copy(p_ev->in6_clat46_ev_arg.euuid, euuid);
d9a64523 1271
0a7de745 1272 nwk_wq_enqueue((struct nwk_wq_entry*)p_ev);
d9a64523 1273}