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