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 | |
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 |
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 | 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 */ |
182 | int |
183 | nat464_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 | */ |
249 | int |
250 | nat464_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 | */ |
464 | int |
465 | nat464_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 | */ |
561 | int |
562 | nat464_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 | |
597 | int |
598 | nat464_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 | */ |
698 | int |
699 | nat464_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 | */ |
746 | int |
747 | nat464_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 | |
1038 | done: |
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 */ |
1070 | static void |
1071 | nat464_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 | |
1134 | uint16_t |
1135 | nat464_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 */ |
1150 | void |
1151 | in6_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 | |
1176 | static void |
1177 | in6_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 | |
1187 | struct 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 | |
1193 | void |
1194 | in6_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 | |