2 * Copyright (c) 2017 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
17 * @brief NAT64 IPv6 to IPv4 translation (inside to outside network)
20 #include <snat/nat64.h>
21 #include <vnet/ip/ip6_to_ip4.h>
22 #include <vnet/fib/fib_table.h>
28 } nat64_in2out_trace_t;
31 format_nat64_in2out_trace (u8 * s, va_list * args)
33 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
34 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
35 nat64_in2out_trace_t *t = va_arg (*args, nat64_in2out_trace_t *);
38 format (s, "NAT64-in2out: sw_if_index %d, next index %d", t->sw_if_index,
44 vlib_node_registration_t nat64_in2out_node;
46 #define foreach_nat64_in2out_error \
47 _(UNSUPPORTED_PROTOCOL, "unsupported protocol") \
48 _(IN2OUT_PACKETS, "good in2out packets processed") \
49 _(NO_TRANSLATION, "no translation") \
54 #define _(sym,str) NAT64_IN2OUT_ERROR_##sym,
55 foreach_nat64_in2out_error
58 } nat64_in2out_error_t;
60 static char *nat64_in2out_error_strings[] = {
61 #define _(sym,string) string,
62 foreach_nat64_in2out_error
68 NAT64_IN2OUT_NEXT_IP4_LOOKUP,
69 NAT64_IN2OUT_NEXT_IP6_LOOKUP,
70 NAT64_IN2OUT_NEXT_DROP,
72 } nat64_in2out_next_t;
74 typedef struct nat64_in2out_set_ctx_t_
78 } nat64_in2out_set_ctx_t;
81 * @brief Check whether is a hairpinning.
83 * If the destination IP address of the packet is an IPv4 address assigned to
84 * the NAT64 itself, then the packet is a hairpin packet.
86 * param dst_addr Destination address of the packet.
88 * @returns 1 if hairpinning, otherwise 0.
90 static_always_inline int
91 is_hairpinning (ip6_address_t * dst_addr)
93 nat64_main_t *nm = &nat64_main;
96 for (i = 0; i < vec_len (nm->addr_pool); i++)
98 if (nm->addr_pool[i].addr.as_u32 == dst_addr->as_u32[3])
106 nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
109 nat64_main_t *nm = &nat64_main;
110 nat64_in2out_set_ctx_t *ctx = arg;
111 nat64_db_bib_entry_t *bibe;
112 nat64_db_st_entry_t *ste;
113 ip46_address_t saddr, daddr;
114 u32 sw_if_index, fib_index;
115 udp_header_t *udp = ip6_next_header (ip6);
116 snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol);
117 u16 sport = udp->src_port;
118 u16 dport = udp->dst_port;
120 sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
122 fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
124 saddr.as_u64[0] = ip6->src_address.as_u64[0];
125 saddr.as_u64[1] = ip6->src_address.as_u64[1];
126 daddr.as_u64[0] = ip6->dst_address.as_u64[0];
127 daddr.as_u64[1] = ip6->dst_address.as_u64[1];
130 nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto,
135 bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
142 nat64_db_bib_entry_find (&nm->db, &saddr, sport, proto, fib_index, 1);
147 ip4_address_t out_addr;
148 if (nat64_alloc_out_addr_and_port
149 (fib_index, proto, &out_addr, &out_port))
153 nat64_db_bib_entry_create (&nm->db, &ip6->src_address, &out_addr,
154 sport, clib_host_to_net_u16 (out_port),
155 fib_index, proto, 0);
160 nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
162 nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
168 nat64_session_reset_timeout (ste, ctx->vm);
170 ip4->src_address.as_u32 = bibe->out_addr.as_u32;
171 udp->src_port = bibe->out_port;
173 ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
175 if (proto == SNAT_PROTOCOL_TCP)
179 tcp_header_t *tcp = ip6_next_header (ip6);
181 checksum = &tcp->checksum;
182 csum = ip_csum_sub_even (*checksum, sport);
183 csum = ip_csum_add_even (csum, udp->src_port);
184 *checksum = ip_csum_fold (csum);
191 nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
193 nat64_main_t *nm = &nat64_main;
194 nat64_in2out_set_ctx_t *ctx = arg;
195 nat64_db_bib_entry_t *bibe;
196 nat64_db_st_entry_t *ste;
197 ip46_address_t saddr, daddr;
198 u32 sw_if_index, fib_index;
199 icmp46_header_t *icmp = ip6_next_header (ip6);
201 sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
203 fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
205 saddr.as_u64[0] = ip6->src_address.as_u64[0];
206 saddr.as_u64[1] = ip6->src_address.as_u64[1];
207 daddr.as_u64[0] = ip6->dst_address.as_u64[0];
208 daddr.as_u64[1] = ip6->dst_address.as_u64[1];
210 if (icmp->type == ICMP4_echo_request || icmp->type == ICMP4_echo_reply)
212 u16 in_id = ((u16 *) (icmp))[2];
214 nat64_db_st_entry_find (&nm->db, &saddr, &daddr, in_id, 0,
215 SNAT_PROTOCOL_ICMP, fib_index, 1);
220 nat64_db_bib_entry_by_index (&nm->db, SNAT_PROTOCOL_ICMP,
228 nat64_db_bib_entry_find (&nm->db, &saddr, in_id,
229 SNAT_PROTOCOL_ICMP, fib_index, 1);
234 ip4_address_t out_addr;
235 if (nat64_alloc_out_addr_and_port
236 (fib_index, SNAT_PROTOCOL_ICMP, &out_addr, &out_id))
240 nat64_db_bib_entry_create (&nm->db, &ip6->src_address,
242 clib_host_to_net_u16 (out_id),
243 fib_index, SNAT_PROTOCOL_ICMP, 0);
248 nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
250 nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
256 nat64_session_reset_timeout (ste, ctx->vm);
258 ip4->src_address.as_u32 = bibe->out_addr.as_u32;
259 ((u16 *) (icmp))[2] = bibe->out_port;
261 ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
265 if (!vec_len (nm->addr_pool))
268 ip4->src_address.as_u32 = nm->addr_pool[0].addr.as_u32;
269 nat64_extract_ip4 (&ip6->dst_address, &ip4->dst_address, fib_index);
276 nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
279 nat64_main_t *nm = &nat64_main;
280 nat64_in2out_set_ctx_t *ctx = arg;
281 nat64_db_st_entry_t *ste;
282 nat64_db_bib_entry_t *bibe;
283 ip46_address_t saddr, daddr;
284 u32 sw_if_index, fib_index;
285 snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol);
287 sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
289 fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
291 saddr.as_u64[0] = ip6->src_address.as_u64[0];
292 saddr.as_u64[1] = ip6->src_address.as_u64[1];
293 daddr.as_u64[0] = ip6->dst_address.as_u64[0];
294 daddr.as_u64[1] = ip6->dst_address.as_u64[1];
296 if (proto == SNAT_PROTOCOL_ICMP)
298 icmp46_header_t *icmp = ip6_next_header (ip6);
299 u16 in_id = ((u16 *) (icmp))[2];
302 (icmp->type == ICMP4_echo_request
303 || icmp->type == ICMP4_echo_reply))
307 nat64_db_st_entry_find (&nm->db, &daddr, &saddr, in_id, 0, proto,
312 bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
316 ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
317 ((u16 *) (icmp))[2] = bibe->out_port;
318 ip4->src_address.as_u32 = ste->out_r_addr.as_u32;
322 udp_header_t *udp = ip6_next_header (ip6);
323 tcp_header_t *tcp = ip6_next_header (ip6);
327 u16 sport = udp->src_port;
328 u16 dport = udp->dst_port;
331 nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto,
336 bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
340 ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
341 udp->dst_port = bibe->out_port;
342 ip4->src_address.as_u32 = ste->out_r_addr.as_u32;
344 if (proto == SNAT_PROTOCOL_TCP)
345 checksum = &tcp->checksum;
347 checksum = &udp->checksum;
348 csum = ip_csum_sub_even (*checksum, dport);
349 csum = ip_csum_add_even (csum, udp->dst_port);
350 *checksum = ip_csum_fold (csum);
357 nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
360 nat64_main_t *nm = &nat64_main;
361 nat64_db_bib_entry_t *bibe;
362 nat64_db_st_entry_t *ste;
363 ip46_address_t saddr, daddr;
364 u32 sw_if_index, fib_index;
365 udp_header_t *udp = ip6_next_header (ip6);
366 tcp_header_t *tcp = ip6_next_header (ip6);
367 snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol);
368 u16 sport = udp->src_port;
369 u16 dport = udp->dst_port;
373 sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
375 fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
377 saddr.as_u64[0] = ip6->src_address.as_u64[0];
378 saddr.as_u64[1] = ip6->src_address.as_u64[1];
379 daddr.as_u64[0] = ip6->dst_address.as_u64[0];
380 daddr.as_u64[1] = ip6->dst_address.as_u64[1];
382 if (proto == SNAT_PROTOCOL_UDP)
383 checksum = &udp->checksum;
385 checksum = &tcp->checksum;
387 csum = ip_csum_sub_even (*checksum, ip6->src_address.as_u64[0]);
388 csum = ip_csum_sub_even (csum, ip6->src_address.as_u64[1]);
389 csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[0]);
390 csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[1]);
391 csum = ip_csum_sub_even (csum, sport);
392 csum = ip_csum_sub_even (csum, dport);
395 nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto,
400 bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
407 nat64_db_bib_entry_find (&nm->db, &saddr, sport, proto, fib_index, 1);
412 ip4_address_t out_addr;
413 if (nat64_alloc_out_addr_and_port
414 (fib_index, proto, &out_addr, &out_port))
418 nat64_db_bib_entry_create (&nm->db, &ip6->src_address, &out_addr,
419 sport, clib_host_to_net_u16 (out_port),
420 fib_index, proto, 0);
425 nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
427 nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
433 nat64_session_reset_timeout (ste, vm);
435 sport = udp->src_port = bibe->out_port;
436 nat64_compose_ip6 (&ip6->src_address, &bibe->out_addr, fib_index);
438 memset (&saddr, 0, sizeof (saddr));
439 memset (&daddr, 0, sizeof (daddr));
440 saddr.ip4.as_u32 = bibe->out_addr.as_u32;
441 daddr.ip4.as_u32 = ste->out_r_addr.as_u32;
444 nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, 0,
449 bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
455 bibe = nat64_db_bib_entry_find (&nm->db, &daddr, dport, proto, 0, 0);
461 nat64_db_st_entry_create (&nm->db, bibe, &ip6->src_address,
465 ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
466 ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
467 udp->dst_port = bibe->in_port;
469 csum = ip_csum_add_even (csum, ip6->src_address.as_u64[0]);
470 csum = ip_csum_add_even (csum, ip6->src_address.as_u64[1]);
471 csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[0]);
472 csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[1]);
473 csum = ip_csum_add_even (csum, udp->src_port);
474 csum = ip_csum_add_even (csum, udp->dst_port);
475 *checksum = ip_csum_fold (csum);
481 nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
484 nat64_main_t *nm = &nat64_main;
485 nat64_db_bib_entry_t *bibe;
486 nat64_db_st_entry_t *ste;
487 icmp46_header_t *icmp = ip6_next_header (ip6);
488 ip6_header_t *inner_ip6;
489 ip46_address_t saddr, daddr;
490 u32 sw_if_index, fib_index;
491 snat_protocol_t proto;
494 u16 *checksum, sport, dport;
497 if (icmp->type == ICMP6_echo_request || icmp->type == ICMP6_echo_reply)
500 inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
502 proto = ip_proto_to_snat_proto (inner_ip6->protocol);
504 if (proto == SNAT_PROTOCOL_ICMP)
507 sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
509 fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
511 saddr.as_u64[0] = inner_ip6->src_address.as_u64[0];
512 saddr.as_u64[1] = inner_ip6->src_address.as_u64[1];
513 daddr.as_u64[0] = inner_ip6->dst_address.as_u64[0];
514 daddr.as_u64[1] = inner_ip6->dst_address.as_u64[1];
516 udp = ip6_next_header (inner_ip6);
517 tcp = ip6_next_header (inner_ip6);
519 sport = udp->src_port;
520 dport = udp->dst_port;
522 if (proto == SNAT_PROTOCOL_UDP)
523 checksum = &udp->checksum;
525 checksum = &tcp->checksum;
527 csum = ip_csum_sub_even (*checksum, inner_ip6->src_address.as_u64[0]);
528 csum = ip_csum_sub_even (csum, inner_ip6->src_address.as_u64[1]);
529 csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[0]);
530 csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[1]);
531 csum = ip_csum_sub_even (csum, sport);
532 csum = ip_csum_sub_even (csum, dport);
535 nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto,
540 bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
544 dport = udp->dst_port = bibe->out_port;
545 nat64_compose_ip6 (&inner_ip6->dst_address, &bibe->out_addr, fib_index);
547 memset (&saddr, 0, sizeof (saddr));
548 memset (&daddr, 0, sizeof (daddr));
549 saddr.ip4.as_u32 = ste->out_r_addr.as_u32;
550 daddr.ip4.as_u32 = bibe->out_addr.as_u32;
553 nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, 0,
558 bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
562 inner_ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0];
563 inner_ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1];
564 udp->src_port = bibe->in_port;
566 csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[0]);
567 csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[1]);
568 csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[0]);
569 csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[1]);
570 csum = ip_csum_add_even (csum, udp->src_port);
571 csum = ip_csum_add_even (csum, udp->dst_port);
572 *checksum = ip_csum_fold (csum);
574 if (!vec_len (nm->addr_pool))
577 nat64_compose_ip6 (&ip6->src_address, &nm->addr_pool[0].addr, fib_index);
578 ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0];
579 ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1];
582 csum = ip_csum_with_carry (0, ip6->payload_length);
583 csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (ip6->protocol));
584 csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[0]);
585 csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[1]);
586 csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[0]);
587 csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[1]);
589 ip_incremental_checksum (csum, icmp,
590 clib_net_to_host_u16 (ip6->payload_length));
591 icmp->checksum = ~ip_csum_fold (csum);
597 nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
598 vlib_frame_t * frame)
600 u32 n_left_from, *from, *to_next;
601 nat64_in2out_next_t next_index;
602 u32 pkts_processed = 0;
604 from = vlib_frame_vector_args (frame);
605 n_left_from = frame->n_vectors;
606 next_index = node->cached_next_index;
608 while (n_left_from > 0)
612 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
614 while (n_left_from > 0 && n_left_to_next > 0)
620 u16 l4_offset0, frag_offset0;
623 nat64_in2out_set_ctx_t ctx0;
625 /* speculatively enqueue b0 to the current next frame */
633 b0 = vlib_get_buffer (vm, bi0);
634 ip60 = vlib_buffer_get_current (b0);
639 next0 = NAT64_IN2OUT_NEXT_IP4_LOOKUP;
643 (ip60, b0->current_length, &l4_protocol0, &l4_offset0,
646 next0 = NAT64_IN2OUT_NEXT_DROP;
647 b0->error = node->errors[NAT64_IN2OUT_ERROR_UNKNOWN];
651 proto0 = ip_proto_to_snat_proto (l4_protocol0);
652 if (PREDICT_FALSE ((proto0 == ~0) || (frag_offset0 != 0)))
654 next0 = NAT64_IN2OUT_NEXT_DROP;
656 node->errors[NAT64_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL];
660 if (proto0 == SNAT_PROTOCOL_ICMP)
662 if (is_hairpinning (&ip60->dst_address))
664 next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP;
665 if (nat64_in2out_icmp_hairpinning (vm, b0, ip60))
667 next0 = NAT64_IN2OUT_NEXT_DROP;
669 node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
675 (b0, nat64_in2out_icmp_set_cb, &ctx0,
676 nat64_in2out_inner_icmp_set_cb, &ctx0))
678 next0 = NAT64_IN2OUT_NEXT_DROP;
679 b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
685 if (is_hairpinning (&ip60->dst_address))
687 next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP;
688 if (nat64_in2out_tcp_udp_hairpinning (vm, b0, ip60))
690 next0 = NAT64_IN2OUT_NEXT_DROP;
692 node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
697 if (ip6_to_ip4_tcp_udp
698 (b0, nat64_in2out_tcp_udp_set_cb, &ctx0, 0))
700 next0 = NAT64_IN2OUT_NEXT_DROP;
701 b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
707 if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
708 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
710 nat64_in2out_trace_t *t =
711 vlib_add_trace (vm, node, b0, sizeof (*t));
712 t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
713 t->next_index = next0;
716 pkts_processed += next0 != NAT64_IN2OUT_NEXT_DROP;
718 /* verify speculative enqueue, maybe switch current next frame */
719 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
720 n_left_to_next, bi0, next0);
722 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
724 vlib_node_increment_counter (vm, nat64_in2out_node.index,
725 NAT64_IN2OUT_ERROR_IN2OUT_PACKETS,
727 return frame->n_vectors;
731 VLIB_REGISTER_NODE (nat64_in2out_node) = {
732 .function = nat64_in2out_node_fn,.name = "nat64-in2out",
733 .vector_size = sizeof (u32),
734 .format_trace = format_nat64_in2out_trace,
735 .type = VLIB_NODE_TYPE_INTERNAL,
736 .n_errors = ARRAY_LEN (nat64_in2out_error_strings),
737 .error_strings = nat64_in2out_error_strings,
739 /* edit / add dispositions here */
741 [NAT64_IN2OUT_NEXT_DROP] = "error-drop",
742 [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup",
743 [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup",
748 VLIB_NODE_FUNCTION_MULTIARCH (nat64_in2out_node, nat64_in2out_node_fn);
751 * fd.io coding-style-patch-verification: ON
754 * eval: (c-set-style "gnu")