diff options
author | Matus Fabian <matfabia@cisco.com> | 2017-06-15 02:28:50 -0700 |
---|---|---|
committer | Ole Trøan <otroan@employees.org> | 2017-06-19 11:08:47 +0000 |
commit | 029f3d2c1c6b04a6cfef17242cb36b304025fe23 (patch) | |
tree | 5036e25543b5d43deeb15fde31135a3531d864ce /src | |
parent | acd4c63e3c6e70ea3f58527d9bace7c0e38df719 (diff) |
NAT64: Hairpinning (VPP-699)
Change-Id: I83a6c277fa211ac2c2ca2d603650c992886af0a7
Signed-off-by: Matus Fabian <matfabia@cisco.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/snat/nat64.c | 73 | ||||
-rw-r--r-- | src/plugins/snat/nat64_cli.c | 2 | ||||
-rw-r--r-- | src/plugins/snat/nat64_in2out.c | 338 | ||||
-rw-r--r-- | src/plugins/snat/nat64_out2in.c | 35 | ||||
-rw-r--r-- | src/plugins/snat/snat_api.c | 4 | ||||
-rw-r--r-- | src/vnet/ip/ip4_to_ip6.h | 55 | ||||
-rw-r--r-- | src/vnet/ip/ip6_to_ip4.h | 22 |
7 files changed, 477 insertions, 52 deletions
diff --git a/src/plugins/snat/nat64.c b/src/plugins/snat/nat64.c index ef745473793..1e8b75967d8 100644 --- a/src/plugins/snat/nat64.c +++ b/src/plugins/snat/nat64.c @@ -95,7 +95,10 @@ nat64_add_del_pool_addr (ip4_address_t * addr, u32 vrf_id, u8 is_add) vec_add2 (nm->addr_pool, a, 1); a->addr = *addr; - a->fib_index = ip4_fib_index_from_table_id (vrf_id); + a->fib_index = 0; + if (vrf_id != ~0) + a->fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id); #define _(N, i, n, s) \ clib_bitmap_alloc (a->busy_##n##_port_bitmap, 65535); foreach_snat_protocol @@ -218,7 +221,7 @@ nat64_alloc_out_addr_and_port (u32 fib_index, snat_protocol_t proto, nat64_main_t *nm = &nat64_main; snat_main_t *sm = &snat_main; int i; - snat_address_t *a; + snat_address_t *a, *ga = 0; u32 portnum; for (i = 0; i < vec_len (nm->addr_pool); i++) @@ -230,22 +233,27 @@ nat64_alloc_out_addr_and_port (u32 fib_index, snat_protocol_t proto, case SNAT_PROTOCOL_##N: \ if (a->busy_##n##_ports < (65535-1024)) \ { \ - while (1) \ + if (a->fib_index == fib_index) \ { \ - portnum = random_u32 (&sm->random_seed); \ - portnum &= 0xFFFF; \ - if (portnum < 1024) \ - continue; \ - if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ - portnum)) \ - continue; \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \ - portnum, 1); \ - a->busy_##n##_ports++; \ - *port = portnum; \ - addr->as_u32 = a->addr.as_u32; \ - return 0; \ - } \ + while (1) \ + { \ + portnum = random_u32 (&sm->random_seed); \ + portnum &= 0xFFFF; \ + if (portnum < 1024) \ + continue; \ + if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ + portnum)) \ + continue; \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \ + portnum, 1); \ + a->busy_##n##_ports++; \ + *port = portnum; \ + addr->as_u32 = a->addr.as_u32; \ + return 0; \ + } \ + } \ + else if (a->fib_index == 0) \ + ga = a; \ } \ break; foreach_snat_protocol @@ -254,8 +262,39 @@ nat64_alloc_out_addr_and_port (u32 fib_index, snat_protocol_t proto, clib_warning ("unknown protocol"); return 1; } + } + if (ga) + { + switch (proto) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + while (1) \ + { \ + portnum = random_u32 (&sm->random_seed); \ + portnum &= 0xFFFF; \ + if (portnum < 1024) \ + continue; \ + if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ + portnum)) \ + continue; \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \ + portnum, 1); \ + a->busy_##n##_ports++; \ + *port = portnum; \ + addr->as_u32 = a->addr.as_u32; \ + return 0; \ + } + break; + foreach_snat_protocol +#undef _ + default: + clib_warning ("unknown protocol"); + return 1; + } } + /* Totally out of translations to use... */ //TODO: IPFix return 1; diff --git a/src/plugins/snat/nat64_cli.c b/src/plugins/snat/nat64_cli.c index 106d9aee41e..e2e3533849f 100644 --- a/src/plugins/snat/nat64_cli.c +++ b/src/plugins/snat/nat64_cli.c @@ -107,7 +107,7 @@ nat64_cli_pool_walk (snat_address_t * ap, void *ctx) if (ap->fib_index != ~0) { fib_table_t *fib; - fib = fib_table_get (ap->fib_index, FIB_PROTOCOL_IP4); + fib = fib_table_get (ap->fib_index, FIB_PROTOCOL_IP6); if (!fib) return -1; vlib_cli_output (vm, " %U tenant VRF: %u", format_ip4_address, diff --git a/src/plugins/snat/nat64_in2out.c b/src/plugins/snat/nat64_in2out.c index eba6932681f..fd32bfc5be7 100644 --- a/src/plugins/snat/nat64_in2out.c +++ b/src/plugins/snat/nat64_in2out.c @@ -21,6 +21,15 @@ #include <vnet/ip/ip6_to_ip4.h> #include <vnet/fib/fib_table.h> +/* *INDENT-OFF* */ +static u8 well_known_prefix[] = { + 0x00, 0x64, 0xff, 0x9b, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; +/* *INDENT-ON* */ + typedef struct { u32 sw_if_index; @@ -65,7 +74,8 @@ static char *nat64_in2out_error_strings[] = { typedef enum { - NAT64_IN2OUT_NEXT_LOOKUP, + NAT64_IN2OUT_NEXT_IP4_LOOKUP, + NAT64_IN2OUT_NEXT_IP6_LOOKUP, NAT64_IN2OUT_NEXT_DROP, NAT64_IN2OUT_N_NEXT, } nat64_in2out_next_t; @@ -76,6 +86,31 @@ typedef struct nat64_in2out_set_ctx_t_ vlib_main_t *vm; } nat64_in2out_set_ctx_t; +/** + * @brief Check whether is a hairpinning. + * + * If the destination IP address of the packet is an IPv4 address assigned to + * the NAT64 itself, then the packet is a hairpin packet. + * + * param dst_addr Destination address of the packet. + * + * @returns 1 if hairpinning, otherwise 0. + */ +static_always_inline int +is_hairpinning (ip6_address_t * dst_addr) +{ + nat64_main_t *nm = &nat64_main; + int i; + + for (i = 0; i < vec_len (nm->addr_pool); i++) + { + if (nm->addr_pool[i].addr.as_u32 == dst_addr->as_u32[3]) + return 1; + } + + return 0; +} + static int nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg) @@ -145,6 +180,18 @@ nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, ip4->dst_address.as_u32 = daddr.ip4.as_u32; + if (proto == SNAT_PROTOCOL_TCP) + { + u16 *checksum; + ip_csum_t csum; + tcp_header_t *tcp = ip6_next_header (ip6); + + checksum = &tcp->checksum; + csum = ip_csum_sub_even (*checksum, sport); + csum = ip_csum_add_even (csum, udp->src_port); + *checksum = ip_csum_fold (csum); + } + return 0; } @@ -279,6 +326,10 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, else { udp_header_t *udp = ip6_next_header (ip6); + tcp_header_t *tcp = ip6_next_header (ip6); + u16 *checksum; + ip_csum_t csum; + u16 sport = udp->src_port; u16 dport = udp->dst_port; @@ -295,11 +346,267 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, ip4->dst_address.as_u32 = bibe->out_addr.as_u32; udp->dst_port = bibe->out_port; ip4->src_address.as_u32 = saddr.ip4.as_u32; + + if (proto == SNAT_PROTOCOL_TCP) + checksum = &tcp->checksum; + else + checksum = &udp->checksum; + csum = ip_csum_sub_even (*checksum, dport); + csum = ip_csum_add_even (csum, udp->dst_port); + *checksum = ip_csum_fold (csum); } return 0; } +static int +nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, + ip6_header_t * ip6) +{ + nat64_main_t *nm = &nat64_main; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + ip46_address_t saddr, daddr; + u32 sw_if_index, fib_index; + udp_header_t *udp = ip6_next_header (ip6); + tcp_header_t *tcp = ip6_next_header (ip6); + snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol); + u16 sport = udp->src_port; + u16 dport = udp->dst_port; + u16 *checksum; + ip_csum_t csum; + + sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + saddr.as_u64[0] = ip6->src_address.as_u64[0]; + saddr.as_u64[1] = ip6->src_address.as_u64[1]; + daddr.as_u64[0] = ip6->dst_address.as_u64[0]; + daddr.as_u64[1] = ip6->dst_address.as_u64[1]; + + if (proto == SNAT_PROTOCOL_UDP) + checksum = &udp->checksum; + else + checksum = &tcp->checksum; + + csum = ip_csum_sub_even (*checksum, ip6->src_address.as_u64[0]); + csum = ip_csum_sub_even (csum, ip6->src_address.as_u64[1]); + csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[0]); + csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[1]); + csum = ip_csum_sub_even (csum, sport); + csum = ip_csum_sub_even (csum, dport); + + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, + fib_index, 1); + + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = + nat64_db_bib_entry_find (&nm->db, &saddr, sport, proto, fib_index, 1); + + if (!bibe) + { + u16 out_port; + ip4_address_t out_addr; + if (nat64_alloc_out_addr_and_port + (fib_index, proto, &out_addr, &out_port)) + return -1; + + bibe = + nat64_db_bib_entry_create (&nm->db, &ip6->src_address, &out_addr, + sport, clib_host_to_net_u16 (out_port), + fib_index, proto, 0); + if (!bibe) + return -1; + } + + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address, + &daddr.ip4, dport); + if (!ste) + return -1; + } + + nat64_session_reset_timeout (ste, vm); + + sport = udp->src_port = bibe->out_port; + memcpy (&ip6->src_address, well_known_prefix, sizeof (ip6_address_t)); + saddr.ip6.as_u32[3] = ip6->src_address.as_u32[3] = bibe->out_addr.as_u32; + + saddr.ip6.as_u32[0] = 0; + saddr.ip6.as_u32[1] = 0; + saddr.ip6.as_u32[2] = 0; + daddr.ip6.as_u32[0] = 0; + daddr.ip6.as_u32[1] = 0; + daddr.ip6.as_u32[2] = 0; + + ste = + nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, 0, + 0); + + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = nat64_db_bib_entry_find (&nm->db, &daddr, dport, proto, 0, 0); + + if (!bibe) + return -1; + + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6->src_address, + &saddr.ip4, sport); + } + + ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0]; + ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; + udp->dst_port = bibe->in_port; + + csum = ip_csum_add_even (csum, ip6->src_address.as_u64[0]); + csum = ip_csum_add_even (csum, ip6->src_address.as_u64[1]); + csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[0]); + csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[1]); + csum = ip_csum_add_even (csum, udp->src_port); + csum = ip_csum_add_even (csum, udp->dst_port); + *checksum = ip_csum_fold (csum); + + return 0; +} + +static int +nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, + ip6_header_t * ip6) +{ + nat64_main_t *nm = &nat64_main; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + icmp46_header_t *icmp = ip6_next_header (ip6); + ip6_header_t *inner_ip6; + ip46_address_t saddr, daddr; + u32 sw_if_index, fib_index; + snat_protocol_t proto; + udp_header_t *udp; + tcp_header_t *tcp; + u16 *checksum, sport, dport; + ip_csum_t csum; + + if (icmp->type == ICMP6_echo_request || icmp->type == ICMP6_echo_reply) + return -1; + + inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8); + + proto = ip_proto_to_snat_proto (inner_ip6->protocol); + + if (proto == SNAT_PROTOCOL_ICMP) + return -1; + + sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + saddr.as_u64[0] = inner_ip6->src_address.as_u64[0]; + saddr.as_u64[1] = inner_ip6->src_address.as_u64[1]; + daddr.as_u64[0] = inner_ip6->dst_address.as_u64[0]; + daddr.as_u64[1] = inner_ip6->dst_address.as_u64[1]; + + udp = ip6_next_header (inner_ip6); + tcp = ip6_next_header (inner_ip6); + + sport = udp->src_port; + dport = udp->dst_port; + + if (proto == SNAT_PROTOCOL_UDP) + checksum = &udp->checksum; + else + checksum = &tcp->checksum; + + csum = ip_csum_sub_even (*checksum, inner_ip6->src_address.as_u64[0]); + csum = ip_csum_sub_even (csum, inner_ip6->src_address.as_u64[1]); + csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[0]); + csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[1]); + csum = ip_csum_sub_even (csum, sport); + csum = ip_csum_sub_even (csum, dport); + + ste = + nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, + fib_index, 1); + if (!ste) + return -1; + + clib_warning (""); + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + + dport = udp->dst_port = bibe->out_port; + memcpy (&inner_ip6->dst_address, well_known_prefix, sizeof (ip6_address_t)); + daddr.ip6.as_u32[3] = inner_ip6->dst_address.as_u32[3] = + bibe->out_addr.as_u32; + + saddr.ip6.as_u32[0] = 0; + saddr.ip6.as_u32[1] = 0; + saddr.ip6.as_u32[2] = 0; + daddr.ip6.as_u32[0] = 0; + daddr.ip6.as_u32[1] = 0; + daddr.ip6.as_u32[2] = 0; + + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, 0, + 0); + if (!ste) + return -1; + + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + + inner_ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0]; + inner_ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1]; + udp->src_port = bibe->in_port; + + csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[0]); + csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[1]); + csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[0]); + csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[1]); + csum = ip_csum_add_even (csum, udp->src_port); + csum = ip_csum_add_even (csum, udp->dst_port); + *checksum = ip_csum_fold (csum); + + if (!vec_len (nm->addr_pool)) + return -1; + + memcpy (&ip6->src_address, well_known_prefix, sizeof (ip6_address_t)); + ip6->src_address.as_u32[3] = nm->addr_pool[0].addr.as_u32; + ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0]; + ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1]; + + icmp->checksum = 0; + csum = ip_csum_with_carry (0, ip6->payload_length); + csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (ip6->protocol)); + csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[0]); + csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[1]); + csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[0]); + csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[1]); + csum = + ip_incremental_checksum (csum, icmp, + clib_net_to_host_u16 (ip6->payload_length)); + icmp->checksum = ~ip_csum_fold (csum); + + return 0; +} + static uword nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) @@ -343,7 +650,7 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, ctx0.b = b0; ctx0.vm = vm; - next0 = NAT64_IN2OUT_NEXT_LOOKUP; + next0 = NAT64_IN2OUT_NEXT_IP4_LOOKUP; if (PREDICT_FALSE (ip6_parse @@ -366,6 +673,18 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, if (proto0 == SNAT_PROTOCOL_ICMP) { + if (is_hairpinning (&ip60->dst_address)) + { + next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP; + if (nat64_in2out_icmp_hairpinning (vm, b0, ip60)) + { + next0 = NAT64_IN2OUT_NEXT_DROP; + b0->error = + node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; + } + goto trace0; + } + if (icmp6_to_icmp (b0, nat64_in2out_icmp_set_cb, &ctx0, nat64_in2out_inner_icmp_set_cb, &ctx0)) @@ -377,6 +696,18 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, } else { + if (is_hairpinning (&ip60->dst_address)) + { + next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP; + if (nat64_in2out_tcp_udp_hairpinning (vm, b0, ip60)) + { + next0 = NAT64_IN2OUT_NEXT_DROP; + b0->error = + node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; + } + goto trace0; + } + if (ip6_to_ip4_tcp_udp (b0, nat64_in2out_tcp_udp_set_cb, &ctx0, 0)) { @@ -422,7 +753,8 @@ VLIB_REGISTER_NODE (nat64_in2out_node) = { /* edit / add dispositions here */ .next_nodes = { [NAT64_IN2OUT_NEXT_DROP] = "error-drop", - [NAT64_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", + [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup", + [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup", }, }; /* *INDENT-ON* */ diff --git a/src/plugins/snat/nat64_out2in.c b/src/plugins/snat/nat64_out2in.c index c11bbb5a7b1..0a5fbe5b357 100644 --- a/src/plugins/snat/nat64_out2in.c +++ b/src/plugins/snat/nat64_out2in.c @@ -27,8 +27,9 @@ static u8 well_known_prefix[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; +}; /* *INDENT-ON* */ + typedef struct { u32 sw_if_index; @@ -95,10 +96,13 @@ nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, ip46_address_t saddr, daddr; ip6_address_t ip6_saddr; udp_header_t *udp = ip4_next_header (ip4); + tcp_header_t *tcp = ip4_next_header (ip4); snat_protocol_t proto = ip_proto_to_snat_proto (ip4->protocol); u16 dport = udp->dst_port; u16 sport = udp->src_port; u32 sw_if_index, fib_index; + u16 *checksum; + ip_csum_t csum; sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); @@ -142,6 +146,16 @@ nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; udp->dst_port = bibe->in_port; + if (proto == SNAT_PROTOCOL_UDP) + checksum = &udp->checksum; + else + checksum = &tcp->checksum; + csum = ip_csum_sub_even (*checksum, dport); + csum = ip_csum_add_even (csum, udp->dst_port); + *checksum = ip_csum_fold (csum); + + vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index; + return 0; } @@ -205,6 +219,7 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg) ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; ((u16 *) (icmp))[2] = bibe->in_port; + vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index; } else { @@ -269,12 +284,17 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0]; ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1]; ((u16 *) (icmp))[2] = bibe->in_port; + + vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index; } else { udp_header_t *udp = ip4_next_header (ip4); + tcp_header_t *tcp = ip4_next_header (ip4); u16 dport = udp->dst_port; u16 sport = udp->src_port; + u16 *checksum; + ip_csum_t csum; ste = nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, @@ -291,6 +311,19 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0]; ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1]; udp->src_port = bibe->in_port; + + if (proto == SNAT_PROTOCOL_UDP) + checksum = &udp->checksum; + else + checksum = &tcp->checksum; + if (*checksum) + { + csum = ip_csum_sub_even (*checksum, sport); + csum = ip_csum_add_even (csum, udp->src_port); + *checksum = ip_csum_fold (csum); + } + + vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index; } return 0; diff --git a/src/plugins/snat/snat_api.c b/src/plugins/snat/snat_api.c index 2a7f115fc44..383df39958b 100644 --- a/src/plugins/snat/snat_api.c +++ b/src/plugins/snat/snat_api.c @@ -1245,7 +1245,7 @@ static void *vl_api_nat64_add_del_pool_addr_range_t_print { u8 *s; - s = format (0, "SCRIPT: nat64_add_del_pool_addr_range"); + s = format (0, "SCRIPT: nat64_add_del_pool_addr_range "); s = format (s, "%U - %U vrf_id %u %s\n", format_ip4_address, mp->start_addr, format_ip4_address, mp->end_addr, @@ -1273,7 +1273,7 @@ nat64_api_pool_walk (snat_address_t * a, void *arg) clib_memcpy (rmp->address, &(a->addr), 4); if (a->fib_index != ~0) { - fib_table_t *fib = fib_table_get (a->fib_index, FIB_PROTOCOL_IP4); + fib_table_t *fib = fib_table_get (a->fib_index, FIB_PROTOCOL_IP6); if (!fib) return -1; rmp->vrf_id = ntohl (fib->ft_table_id); diff --git a/src/vnet/ip/ip4_to_ip6.h b/src/vnet/ip/ip4_to_ip6.h index dad35230b12..cdd370723f2 100644 --- a/src/vnet/ip/ip4_to_ip6.h +++ b/src/vnet/ip/ip4_to_ip6.h @@ -321,17 +321,9 @@ icmp_to_icmp6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx, //We have an ICMP inside an ICMP //It needs to be translated, but not for error ICMP messages icmp46_header_t *inner_icmp = (icmp46_header_t *) (inner_ip4 + 1); - csum = inner_icmp->checksum; //Only types ICMP4_echo_request and ICMP4_echo_reply are handled by icmp_to_icmp6_header - csum = ip_csum_sub_even (csum, *((u16 *) inner_icmp)); inner_icmp->type = (inner_icmp->type == ICMP4_echo_request) ? ICMP6_echo_request : ICMP6_echo_reply; - csum = ip_csum_add_even (csum, *((u16 *) inner_icmp)); - csum = - ip_csum_add_even (csum, clib_host_to_net_u16 (IP_PROTOCOL_ICMP6)); - csum = - ip_csum_add_even (csum, inner_ip4->length - sizeof (*inner_ip4)); - inner_icmp->checksum = ip_csum_fold (csum); inner_L4_checksum = &inner_icmp->checksum; inner_ip4->protocol = IP_PROTOCOL_ICMP6; } @@ -341,8 +333,6 @@ icmp_to_icmp6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx, os_panic (); } - csum = *inner_L4_checksum; //Initial checksum of the inner L4 header - inner_ip6->ip_version_traffic_class_and_flow_label = clib_host_to_net_u32 ((6 << 28) + (inner_ip4->tos << 20)); inner_ip6->payload_length = @@ -367,14 +357,42 @@ icmp_to_icmp6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx, sizeof (*inner_frag)); } - /* UDP checksum is optional */ - if (csum) + csum = *inner_L4_checksum; + if (inner_ip6->protocol == IP_PROTOCOL_ICMP6) { - csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[0]); - csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[1]); - csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[0]); - csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[1]); - *inner_L4_checksum = ip_csum_fold (csum); + //Recompute ICMP checksum + icmp46_header_t *inner_icmp = (icmp46_header_t *) (inner_ip4 + 1); + + inner_icmp->checksum = 0; + csum = ip_csum_with_carry (0, inner_ip6->payload_length); + csum = + ip_csum_with_carry (csum, + clib_host_to_net_u16 (inner_ip6->protocol)); + csum = ip_csum_with_carry (csum, inner_ip6->src_address.as_u64[0]); + csum = ip_csum_with_carry (csum, inner_ip6->src_address.as_u64[1]); + csum = ip_csum_with_carry (csum, inner_ip6->dst_address.as_u64[0]); + csum = ip_csum_with_carry (csum, inner_ip6->dst_address.as_u64[1]); + csum = + ip_incremental_checksum (csum, inner_icmp, + clib_net_to_host_u16 + (inner_ip6->payload_length)); + inner_icmp->checksum = ~ip_csum_fold (csum); + } + else + { + /* UDP checksum is optional */ + if (csum) + { + csum = + ip_csum_add_even (csum, inner_ip6->src_address.as_u64[0]); + csum = + ip_csum_add_even (csum, inner_ip6->src_address.as_u64[1]); + csum = + ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[0]); + csum = + ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[1]); + *inner_L4_checksum = ip_csum_fold (csum); + } } } else @@ -518,6 +536,7 @@ ip4_to_ip6_tcp_udp (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx) csum = ip_csum_sub_even (*checksum, ip4->src_address.as_u32); csum = ip_csum_sub_even (csum, ip4->dst_address.as_u32); + *checksum = ip_csum_fold (csum); // Deal with fragmented packets if (PREDICT_FALSE (ip4->flags_and_fragment_offset & @@ -558,7 +577,7 @@ ip4_to_ip6_tcp_udp (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx) if ((rv = fn (ip4, ip6, ctx)) != 0) return rv; - csum = ip_csum_add_even (csum, ip6->src_address.as_u64[0]); + csum = ip_csum_add_even (*checksum, ip6->src_address.as_u64[0]); csum = ip_csum_add_even (csum, ip6->src_address.as_u64[1]); csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[0]); csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[1]); diff --git a/src/vnet/ip/ip6_to_ip4.h b/src/vnet/ip/ip6_to_ip4.h index 92f73ba165d..7a0d534959c 100644 --- a/src/vnet/ip/ip6_to_ip4.h +++ b/src/vnet/ip/ip6_to_ip4.h @@ -314,13 +314,9 @@ icmp6_to_icmp (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx, else if (inner_protocol == IP_PROTOCOL_ICMP6) { icmp46_header_t *inner_icmp = (icmp46_header_t *) inner_l4; - csum = inner_icmp->checksum; - csum = ip_csum_sub_even (csum, *((u16 *) inner_icmp)); //It cannot be of a different type as ip6_icmp_to_icmp6_in_place succeeded inner_icmp->type = (inner_icmp->type == ICMP6_echo_request) ? ICMP4_echo_request : ICMP4_echo_reply; - csum = ip_csum_add_even (csum, *((u16 *) inner_icmp)); - inner_icmp->checksum = ip_csum_fold (csum); inner_protocol = IP_PROTOCOL_ICMP; //Will be copied to ip6 later inner_L4_checksum = &inner_icmp->checksum; } @@ -334,6 +330,7 @@ icmp6_to_icmp (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx, csum = ip_csum_sub_even (csum, inner_ip6->src_address.as_u64[1]); csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[0]); csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[1]); + *inner_L4_checksum = ip_csum_fold (csum); if ((rv = inner_fn (inner_ip6, inner_ip4, inner_ctx)) != 0) return rv; @@ -353,19 +350,23 @@ icmp6_to_icmp (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx, if (inner_ip4->protocol == IP_PROTOCOL_ICMP) { - //Remove remainings of the pseudo-header in the csum - csum = - ip_csum_sub_even (csum, clib_host_to_net_u16 (IP_PROTOCOL_ICMP6)); + //Recompute ICMP checksum + icmp46_header_t *inner_icmp = (icmp46_header_t *) inner_l4; + inner_icmp->checksum = 0; csum = - ip_csum_sub_even (csum, inner_ip4->length - sizeof (*inner_ip4)); + ip_incremental_checksum (0, inner_icmp, + clib_net_to_host_u16 (inner_ip4->length) + - sizeof (*inner_ip4)); + inner_icmp->checksum = ~ip_csum_fold (csum); } else { //Update to new pseudo-header + csum = *inner_L4_checksum; csum = ip_csum_add_even (csum, inner_ip4->src_address.as_u32); csum = ip_csum_add_even (csum, inner_ip4->dst_address.as_u32); + *inner_L4_checksum = ip_csum_fold (csum); } - *inner_L4_checksum = ip_csum_fold (csum); //Move up icmp header ip4 = (ip4_header_t *) u8_ptr_add (inner_l4, -2 * sizeof (*ip4) - 8); @@ -512,6 +513,7 @@ ip6_to_ip4_tcp_udp (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx, csum = ip_csum_sub_even (csum, ip6->src_address.as_u64[1]); csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[0]); csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[1]); + *checksum = ip_csum_fold (csum); no_csum: ip4 = (ip4_header_t *) u8_ptr_add (ip6, l4_offset - sizeof (*ip4)); @@ -552,7 +554,7 @@ no_csum: } else { - csum = ip_csum_add_even (csum, ip4->dst_address.as_u32); + csum = ip_csum_add_even (*checksum, ip4->dst_address.as_u32); csum = ip_csum_add_even (csum, ip4->src_address.as_u32); *checksum = ip_csum_fold (csum); } |