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