From c5c6a3342f3cbaaac647d52fd960b6f5b0fcd4e0 Mon Sep 17 00:00:00 2001 From: Juraj Sloboda Date: Tue, 9 Jan 2018 16:08:32 +0100 Subject: [PATCH] Add basic support for DS-Lite CE (VPP-1059) Change-Id: Ifcca60da3f77c0a4959f98b3365c846badbdc2d0 Signed-off-by: Juraj Sloboda --- src/plugins/nat.am | 4 +- src/plugins/nat/dslite.c | 109 +++++++++++++++++++++++++--- src/plugins/nat/dslite.h | 20 ++++++ src/plugins/nat/dslite_ce_decap.c | 146 ++++++++++++++++++++++++++++++++++++++ src/plugins/nat/dslite_ce_encap.c | 138 +++++++++++++++++++++++++++++++++++ src/plugins/nat/dslite_cli.c | 62 ++++++++++++++++ src/plugins/nat/dslite_dpo.c | 50 +++++++++++++ src/plugins/nat/dslite_dpo.h | 2 + src/plugins/nat/nat.api | 63 ++++++++++++++++ src/plugins/nat/nat.c | 3 + src/plugins/nat/nat_api.c | 99 +++++++++++++++++++++++++- test/test_nat.py | 104 +++++++++++++++++++++++++++ test/vpp_papi_provider.py | 11 +++ 13 files changed, 798 insertions(+), 13 deletions(-) create mode 100644 src/plugins/nat/dslite_ce_decap.c create mode 100644 src/plugins/nat/dslite_ce_encap.c diff --git a/src/plugins/nat.am b/src/plugins/nat.am index a0e0568a5a1..fa3c4b9fbc3 100644 --- a/src/plugins/nat.am +++ b/src/plugins/nat.am @@ -33,7 +33,9 @@ nat_plugin_la_SOURCES = nat/nat.c \ nat/dslite.c \ nat/dslite_in2out.c \ nat/dslite_out2in.c \ - nat/dslite_cli.c + nat/dslite_cli.c \ + nat/dslite_ce_encap.c \ + nat/dslite_ce_decap.c API_FILES += nat/nat.api diff --git a/src/plugins/nat/dslite.c b/src/plugins/nat/dslite.c index c86cc8d7e1d..12c813200ec 100644 --- a/src/plugins/nat/dslite.c +++ b/src/plugins/nat/dslite.c @@ -65,30 +65,105 @@ dslite_init (vlib_main_t * vm) } /* *INDENT-ON* */ + dm->is_ce = 0; + dslite_dpo_module_init (); } +void +dslite_set_ce (dslite_main_t * dm, u8 set) +{ + dm->is_ce = (set != 0); +} + int dslite_set_aftr_ip6_addr (dslite_main_t * dm, ip6_address_t * addr) { - dpo_id_t dpo_v6 = DPO_INVALID; + dpo_id_t dpo = DPO_INVALID; - dslite_dpo_create (DPO_PROTO_IP6, 0, &dpo_v6); - fib_prefix_t pfx = { - .fp_proto = FIB_PROTOCOL_IP6, - .fp_len = 128, - .fp_addr.ip6.as_u64[0] = addr->as_u64[0], - .fp_addr.ip6.as_u64[1] = addr->as_u64[1], - }; - fib_table_entry_special_dpo_add (0, &pfx, FIB_SOURCE_PLUGIN_HI, - FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v6); - dpo_reset (&dpo_v6); + if (dm->is_ce) + { + dslite_ce_dpo_create (DPO_PROTO_IP4, 0, &dpo); + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP4, + .fp_len = 0, + .fp_addr.ip4.as_u32 = 0, + }; + fib_table_entry_special_dpo_add (0, &pfx, FIB_SOURCE_PLUGIN_HI, + FIB_ENTRY_FLAG_EXCLUSIVE, &dpo); + } + else + { + dslite_dpo_create (DPO_PROTO_IP6, 0, &dpo); + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP6, + .fp_len = 128, + .fp_addr.ip6.as_u64[0] = addr->as_u64[0], + .fp_addr.ip6.as_u64[1] = addr->as_u64[1], + }; + fib_table_entry_special_dpo_add (0, &pfx, FIB_SOURCE_PLUGIN_HI, + FIB_ENTRY_FLAG_EXCLUSIVE, &dpo); + } + + dpo_reset (&dpo); dm->aftr_ip6_addr.as_u64[0] = addr->as_u64[0]; dm->aftr_ip6_addr.as_u64[1] = addr->as_u64[1]; return 0; } +int +dslite_set_aftr_ip4_addr (dslite_main_t * dm, ip4_address_t * addr) +{ + dm->aftr_ip4_addr.as_u32 = addr->as_u32; + return 0; +} + +int +dslite_set_b4_ip6_addr (dslite_main_t * dm, ip6_address_t * addr) +{ + if (dm->is_ce) + { + dpo_id_t dpo = DPO_INVALID; + + dslite_ce_dpo_create (DPO_PROTO_IP6, 0, &dpo); + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP6, + .fp_len = 128, + .fp_addr.ip6.as_u64[0] = addr->as_u64[0], + .fp_addr.ip6.as_u64[1] = addr->as_u64[1], + }; + fib_table_entry_special_dpo_add (0, &pfx, FIB_SOURCE_PLUGIN_HI, + FIB_ENTRY_FLAG_EXCLUSIVE, &dpo); + + dpo_reset (&dpo); + + dm->b4_ip6_addr.as_u64[0] = addr->as_u64[0]; + dm->b4_ip6_addr.as_u64[1] = addr->as_u64[1]; + } + else + { + return VNET_API_ERROR_FEATURE_DISABLED; + } + + return 0; +} + +int +dslite_set_b4_ip4_addr (dslite_main_t * dm, ip4_address_t * addr) +{ + if (dm->is_ce) + { + dm->b4_ip4_addr.as_u32 = addr->as_u32; + } + else + { + return VNET_API_ERROR_FEATURE_DISABLED; + } + + return 0; +} + int dslite_add_del_pool_addr (dslite_main_t * dm, ip4_address_t * addr, u8 is_add) { @@ -155,6 +230,18 @@ format_dslite_trace (u8 * s, va_list * args) return s; } +u8 * +format_dslite_ce_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + dslite_ce_trace_t *t = va_arg (*args, dslite_ce_trace_t *); + + s = format (s, "next index %d", t->next_index); + + return s; +} + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/plugins/nat/dslite.h b/src/plugins/nat/dslite.h index 4b7089b3190..6a1c2ecd709 100644 --- a/src/plugins/nat/dslite.h +++ b/src/plugins/nat/dslite.h @@ -80,11 +80,17 @@ typedef struct { ip6_address_t aftr_ip6_addr; ip4_address_t aftr_ip4_addr; + ip6_address_t b4_ip6_addr; + ip4_address_t b4_ip4_addr; dslite_per_thread_data_t *per_thread_data; snat_address_t *addr_pool; u32 num_workers; u32 first_worker_index; u16 port_per_thread; + + /* If set then the DSLite component behaves as CPE/B4 + * otherwise it behaves as AFTR */ + u8 is_ce; } dslite_main_t; typedef struct @@ -93,9 +99,16 @@ typedef struct u32 session_index; } dslite_trace_t; +typedef struct +{ + u32 next_index; +} dslite_ce_trace_t; + #define foreach_dslite_error \ _(IN2OUT, "valid in2out DS-Lite packets") \ _(OUT2IN, "valid out2in DS-Lite packets") \ +_(CE_ENCAP, "valid CE encap DS-Lite packets") \ +_(CE_DECAP, "valid CE decap DS-Lite packets") \ _(NO_TRANSLATION, "no translation") \ _(BAD_IP6_PROTOCOL, "bad ip6 protocol") \ _(OUT_OF_PORTS, "out of ports") \ @@ -115,12 +128,19 @@ extern dslite_main_t dslite_main; extern vlib_node_registration_t dslite_in2out_node; extern vlib_node_registration_t dslite_in2out_slowpath_node; extern vlib_node_registration_t dslite_out2in_node; +extern vlib_node_registration_t dslite_ce_encap_node; +extern vlib_node_registration_t dslite_ce_decap_node; void dslite_init (vlib_main_t * vm); +void dslite_set_ce (dslite_main_t * dm, u8 set); int dslite_set_aftr_ip6_addr (dslite_main_t * dm, ip6_address_t * addr); +int dslite_set_b4_ip6_addr (dslite_main_t * dm, ip6_address_t * addr); +int dslite_set_aftr_ip4_addr (dslite_main_t * dm, ip4_address_t * addr); +int dslite_set_b4_ip4_addr (dslite_main_t * dm, ip4_address_t * addr); int dslite_add_del_pool_addr (dslite_main_t * dm, ip4_address_t * addr, u8 is_add); u8 *format_dslite_trace (u8 * s, va_list * args); +u8 *format_dslite_ce_trace (u8 * s, va_list * args); #endif /* __included_dslite_h__ */ diff --git a/src/plugins/nat/dslite_ce_decap.c b/src/plugins/nat/dslite_ce_decap.c new file mode 100644 index 00000000000..615a424e871 --- /dev/null +++ b/src/plugins/nat/dslite_ce_decap.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +vlib_node_registration_t dslite_ce_decap_node; + +typedef enum +{ + DSLITE_CE_DECAP_NEXT_IP4_LOOKUP, + DSLITE_IN2OUT_NEXT_IP6_ICMP, + DSLITE_CE_DECAP_NEXT_DROP, + DSLITE_CE_DECAP_N_NEXT, +} dslite_ce_decap_next_t; + +static char *dslite_ce_decap_error_strings[] = { +#define _(sym,string) string, + foreach_dslite_error +#undef _ +}; + +static uword +dslite_ce_decap_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + dslite_ce_decap_next_t next_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0 = DSLITE_CE_DECAP_NEXT_IP4_LOOKUP; + u8 error0 = DSLITE_ERROR_CE_DECAP; + ip4_header_t *ip40; + ip6_header_t *ip60; + u32 proto0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + ip60 = vlib_buffer_get_current (b0); + + if (PREDICT_FALSE (ip60->protocol != IP_PROTOCOL_IP_IN_IP)) + { + if (ip60->protocol == IP_PROTOCOL_ICMP6) + { + next0 = DSLITE_IN2OUT_NEXT_IP6_ICMP; + goto trace0; + } + error0 = DSLITE_ERROR_BAD_IP6_PROTOCOL; + next0 = DSLITE_CE_DECAP_NEXT_DROP; + goto trace0; + } + + ip40 = vlib_buffer_get_current (b0) + sizeof (ip6_header_t); + proto0 = ip_proto_to_snat_proto (ip40->protocol); + + if (PREDICT_FALSE (proto0 == ~0)) + { + error0 = DSLITE_ERROR_UNSUPPORTED_PROTOCOL; + next0 = DSLITE_CE_DECAP_NEXT_DROP; + goto trace0; + } + + ip40->tos = + (clib_net_to_host_u32 + (ip60->ip_version_traffic_class_and_flow_label) & 0x0ff00000) >> + 20; + vlib_buffer_advance (b0, sizeof (ip6_header_t)); + + trace0: + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + dslite_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next_index = next0; + } + + b0->error = node->errors[error0]; + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (dslite_ce_decap_node) = { + .function = dslite_ce_decap_node_fn, + .name = "dslite-ce-decap", + .vector_size = sizeof (u32), + .format_trace = format_dslite_ce_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (dslite_ce_decap_error_strings), + .error_strings = dslite_ce_decap_error_strings, + .n_next_nodes = DSLITE_CE_DECAP_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = { + [DSLITE_CE_DECAP_NEXT_DROP] = "error-drop", + [DSLITE_CE_DECAP_NEXT_IP4_LOOKUP] = "ip4-lookup", + [DSLITE_IN2OUT_NEXT_IP6_ICMP] = "ip6-icmp-input", + }, +}; +/* *INDENT-ON* */ + +VLIB_NODE_FUNCTION_MULTIARCH (dslite_ce_decap_node, dslite_ce_decap_node_fn); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/dslite_ce_encap.c b/src/plugins/nat/dslite_ce_encap.c new file mode 100644 index 00000000000..f098d75ca70 --- /dev/null +++ b/src/plugins/nat/dslite_ce_encap.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +vlib_node_registration_t dslite_ce_encap_node; + +typedef enum +{ + DSLITE_CE_ENCAP_NEXT_IP6_LOOKUP, + DSLITE_CE_ENCAP_NEXT_DROP, + DSLITE_CE_ENCAP_N_NEXT, +} dslite_ce_encap_next_t; + +static char *dslite_ce_encap_error_strings[] = { +#define _(sym,string) string, + foreach_dslite_error +#undef _ +}; + +static uword +dslite_ce_encap_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + dslite_ce_encap_next_t next_index; + dslite_main_t *dm = &dslite_main; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0 = DSLITE_CE_ENCAP_NEXT_IP6_LOOKUP; + u8 error0 = DSLITE_ERROR_CE_ENCAP; + ip4_header_t *ip40; + ip6_header_t *ip60; + u32 proto0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + ip40 = vlib_buffer_get_current (b0); + proto0 = ip_proto_to_snat_proto (ip40->protocol); + + if (PREDICT_FALSE (proto0 == ~0)) + { + error0 = DSLITE_ERROR_UNSUPPORTED_PROTOCOL; + next0 = DSLITE_CE_ENCAP_NEXT_DROP; + goto trace0; + } + + /* Construct IPv6 header */ + vlib_buffer_advance (b0, -(sizeof (ip6_header_t))); + ip60 = vlib_buffer_get_current (b0); + ip60->ip_version_traffic_class_and_flow_label = + clib_host_to_net_u32 ((6 << 28) + (ip40->tos << 20)); + ip60->payload_length = ip40->length; + ip60->protocol = IP_PROTOCOL_IP_IN_IP; + ip60->hop_limit = ip40->ttl; + ip60->dst_address.as_u64[0] = dm->aftr_ip6_addr.as_u64[0]; + ip60->dst_address.as_u64[1] = dm->aftr_ip6_addr.as_u64[1]; + ip60->src_address.as_u64[0] = dm->b4_ip6_addr.as_u64[0]; + ip60->src_address.as_u64[1] = dm->b4_ip6_addr.as_u64[1]; + + trace0: + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + dslite_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next_index = next0; + } + + b0->error = node->errors[error0]; + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (dslite_ce_encap_node) = { + .function = dslite_ce_encap_node_fn, + .name = "dslite-ce-encap", + .vector_size = sizeof (u32), + .format_trace = format_dslite_ce_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (dslite_ce_encap_error_strings), + .error_strings = dslite_ce_encap_error_strings, + .n_next_nodes = DSLITE_CE_ENCAP_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = { + [DSLITE_CE_ENCAP_NEXT_DROP] = "error-drop", + [DSLITE_CE_ENCAP_NEXT_IP6_LOOKUP] = "ip6-lookup", + }, +}; +/* *INDENT-ON* */ + +VLIB_NODE_FUNCTION_MULTIARCH (dslite_ce_encap_node, dslite_ce_encap_node_fn); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/dslite_cli.c b/src/plugins/nat/dslite_cli.c index 7282762ebfc..f600c0350d6 100644 --- a/src/plugins/nat/dslite_cli.c +++ b/src/plugins/nat/dslite_cli.c @@ -161,6 +161,56 @@ dslite_show_aftr_ip6_addr_command_fn (vlib_main_t * vm, return 0; } +static clib_error_t * +dslite_set_b4_tunnel_addr_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + dslite_main_t *dm = &dslite_main; + unformat_input_t _line_input, *line_input = &_line_input; + ip6_address_t ip6_addr; + int rv; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%U", unformat_ip6_address, &ip6_addr)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + rv = dslite_set_b4_ip6_addr (dm, &ip6_addr); + + if (rv) + error = + clib_error_return (0, "Set DS-Lite B4 tunnel endpoint address failed."); + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +dslite_show_b4_ip6_addr_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + dslite_main_t *dm = &dslite_main; + + vlib_cli_output (vm, "%U", format_ip6_address, &dm->b4_ip6_addr); + return 0; +} + static u8 * format_dslite_session (u8 * s, va_list * args) { @@ -263,6 +313,18 @@ VLIB_CLI_COMMAND (dslite_show_aftr_ip6_addr, static) = { .function = dslite_show_aftr_ip6_addr_command_fn, }; +VLIB_CLI_COMMAND (dslite_set_b4_tunnel_addr, static) = { + .path = "dslite set b4-tunnel-endpoint-address", + .short_help = "dslite set b4-tunnel-endpoint-address ", + .function = dslite_set_b4_tunnel_addr_command_fn, +}; + +VLIB_CLI_COMMAND (dslite_show_b4_ip6_addr, static) = { + .path = "show dslite b4-tunnel-endpoint-address", + .short_help = "show dslite b4-tunnel-endpoint-address", + .function = dslite_show_b4_ip6_addr_command_fn, +}; + VLIB_CLI_COMMAND (dslite_show_sessions, static) = { .path = "show dslite sessions", .short_help = "show dslite sessions", diff --git a/src/plugins/nat/dslite_dpo.c b/src/plugins/nat/dslite_dpo.c index 376d7174858..97ebb18cf70 100644 --- a/src/plugins/nat/dslite_dpo.c +++ b/src/plugins/nat/dslite_dpo.c @@ -17,6 +17,7 @@ #include dpo_type_t dslite_dpo_type; +dpo_type_t dslite_ce_dpo_type; void dslite_dpo_create (dpo_proto_t dproto, u32 aftr_index, dpo_id_t * dpo) @@ -24,6 +25,12 @@ dslite_dpo_create (dpo_proto_t dproto, u32 aftr_index, dpo_id_t * dpo) dpo_set (dpo, dslite_dpo_type, dproto, aftr_index); } +void +dslite_ce_dpo_create (dpo_proto_t dproto, u32 b4_index, dpo_id_t * dpo) +{ + dpo_set (dpo, dslite_ce_dpo_type, dproto, b4_index); +} + u8 * format_dslite_dpo (u8 * s, va_list * args) { @@ -33,6 +40,15 @@ format_dslite_dpo (u8 * s, va_list * args) return (format (s, "DS-Lite: AFTR:%d", index)); } +u8 * +format_dslite_ce_dpo (u8 * s, va_list * args) +{ + index_t index = va_arg (*args, index_t); + CLIB_UNUSED (u32 indent) = va_arg (*args, u32); + + return (format (s, "DS-Lite: B4:%d", index)); +} + static void dslite_dpo_lock (dpo_id_t * dpo) { @@ -43,12 +59,28 @@ dslite_dpo_unlock (dpo_id_t * dpo) { } +static void +dslite_ce_dpo_lock (dpo_id_t * dpo) +{ +} + +static void +dslite_ce_dpo_unlock (dpo_id_t * dpo) +{ +} + const static dpo_vft_t dslite_dpo_vft = { .dv_lock = dslite_dpo_lock, .dv_unlock = dslite_dpo_unlock, .dv_format = format_dslite_dpo, }; +const static dpo_vft_t dslite_ce_dpo_vft = { + .dv_lock = dslite_ce_dpo_lock, + .dv_unlock = dslite_ce_dpo_unlock, + .dv_format = format_dslite_ce_dpo, +}; + const static char *const dslite_ip4_nodes[] = { "dslite-out2in", NULL, @@ -59,16 +91,34 @@ const static char *const dslite_ip6_nodes[] = { NULL, }; +const static char *const dslite_ce_ip4_nodes[] = { + "dslite-ce-encap", + NULL, +}; + +const static char *const dslite_ce_ip6_nodes[] = { + "dslite-ce-decap", + NULL, +}; + const static char *const *const dslite_nodes[DPO_PROTO_NUM] = { [DPO_PROTO_IP4] = dslite_ip4_nodes, [DPO_PROTO_IP6] = dslite_ip6_nodes, [DPO_PROTO_MPLS] = NULL, }; +const static char *const *const dslite_ce_nodes[DPO_PROTO_NUM] = { + [DPO_PROTO_IP4] = dslite_ce_ip4_nodes, + [DPO_PROTO_IP6] = dslite_ce_ip6_nodes, + [DPO_PROTO_MPLS] = NULL, +}; + void dslite_dpo_module_init (void) { dslite_dpo_type = dpo_register_new_type (&dslite_dpo_vft, dslite_nodes); + dslite_ce_dpo_type = dpo_register_new_type (&dslite_ce_dpo_vft, + dslite_ce_nodes); } /* diff --git a/src/plugins/nat/dslite_dpo.h b/src/plugins/nat/dslite_dpo.h index ead0e7de610..53d37b27fb4 100644 --- a/src/plugins/nat/dslite_dpo.h +++ b/src/plugins/nat/dslite_dpo.h @@ -20,8 +20,10 @@ #include void dslite_dpo_create (dpo_proto_t dproto, u32 aftr_index, dpo_id_t * dpo); +void dslite_ce_dpo_create (dpo_proto_t dproto, u32 b4_index, dpo_id_t * dpo); u8 *format_dslite_dpo (u8 * s, va_list * args); +u8 *format_dslite_ce_dpo (u8 * s, va_list * args); void dslite_dpo_module_init (void); diff --git a/src/plugins/nat/nat.api b/src/plugins/nat/nat.api index d6a912b72d0..6ebe4e37933 100644 --- a/src/plugins/nat/nat.api +++ b/src/plugins/nat/nat.api @@ -1159,9 +1159,72 @@ autoreply define dslite_add_del_pool_addr_range { u8 is_add; }; +/** \brief Set AFTR IPv6 and IPv4 addresses + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param ip4_addr - IPv4 address + @param ip6_addr - IP64 address +*/ autoreply define dslite_set_aftr_addr { u32 client_index; u32 context; u8 ip4_addr[4]; u8 ip6_addr[16]; }; + +/** \brief Get AFTR IPv6 and IPv4 addresses + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define dslite_get_aftr_addr { + u32 client_index; + u32 context; +}; + +/** \brief Response to get AFTR IPv6 and IPv4 addresses + @param context - sender context, to match reply w/ request + @param retval - return code + @param ip4_addr - IPv4 address + @param ip6_addr - IP64 address +*/ +define dslite_get_aftr_addr_reply { + u32 context; + i32 retval; + u8 ip4_addr[4]; + u8 ip6_addr[16]; +}; + +/** \brief Set B4 IPv6 and IPv4 addresses + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param ip4_addr - IPv4 address + @param ip6_addr - IP64 address +*/ +autoreply define dslite_set_b4_addr { + u32 client_index; + u32 context; + u8 ip4_addr[4]; + u8 ip6_addr[16]; +}; + +/** \brief Get B4 IPv6 and IPv4 addresses + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define dslite_get_b4_addr { + u32 client_index; + u32 context; +}; + +/** \brief Response to get B4 IPv6 and IPv4 addresses + @param context - sender context, to match reply w/ request + @param retval - return code + @param ip4_addr - IPv4 address + @param ip6_addr - IP64 address +*/ +define dslite_get_b4_addr_reply { + u32 context; + i32 retval; + u8 ip4_addr[4]; + u8 ip6_addr[16]; +}; diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 9993de06128..90515acc5b6 100644 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -2900,6 +2900,7 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) u8 static_mapping_only = 0; u8 static_mapping_connection_tracking = 0; snat_main_per_thread_data_t *tsm; + dslite_main_t * dm = &dslite_main; sm->deterministic = 0; sm->out2in_dpo = 0; @@ -2945,6 +2946,8 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) ; else if (unformat (input, "out2in dpo")) sm->out2in_dpo = 1; + else if (unformat (input, "dslite ce")) + dslite_set_ce(dm, 1); else return clib_error_return (0, "unknown input '%U'", format_unformat_error, input); diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c index bc192a782f6..d9f8bbd41be 100644 --- a/src/plugins/nat/nat_api.c +++ b/src/plugins/nat/nat_api.c @@ -2385,10 +2385,14 @@ vl_api_dslite_set_aftr_addr_t_handler (vl_api_dslite_set_aftr_addr_t * mp) dslite_main_t *dm = &dslite_main; int rv = 0; ip6_address_t ip6_addr; + ip4_address_t ip4_addr; memcpy (&ip6_addr.as_u8, mp->ip6_addr, 16); + memcpy (&ip4_addr.as_u8, mp->ip4_addr, 4); rv = dslite_set_aftr_ip6_addr (dm, &ip6_addr); + if (rv == 0) + rv = dslite_set_aftr_ip4_addr (dm, &ip4_addr); REPLY_MACRO (VL_API_DSLITE_SET_AFTR_ADDR_REPLY); } @@ -2407,6 +2411,96 @@ vl_api_dslite_set_aftr_addr_t_print (vl_api_dslite_set_aftr_addr_t * mp, FINISH; } +static void +vl_api_dslite_get_aftr_addr_t_handler (vl_api_dslite_get_aftr_addr_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_dslite_get_aftr_addr_reply_t *rmp; + dslite_main_t *dm = &dslite_main; + int rv = 0; + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_DSLITE_GET_AFTR_ADDR_REPLY, + ({ + memcpy (rmp->ip4_addr, &dm->aftr_ip4_addr.as_u8, 4); + memcpy (rmp->ip6_addr, &dm->aftr_ip6_addr.as_u8, 16); + })) + /* *INDENT-ON* */ +} + +static void * +vl_api_dslite_get_aftr_addr_t_print (vl_api_dslite_get_aftr_addr_t * mp, + void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: dslite_get_aftr_addr"); + + FINISH; +} + +static void +vl_api_dslite_set_b4_addr_t_handler (vl_api_dslite_set_b4_addr_t * mp) +{ + vl_api_dslite_set_b4_addr_reply_t *rmp; + snat_main_t *sm = &snat_main; + dslite_main_t *dm = &dslite_main; + int rv = 0; + ip6_address_t ip6_addr; + ip4_address_t ip4_addr; + + memcpy (&ip6_addr.as_u8, mp->ip6_addr, 16); + memcpy (&ip4_addr.as_u8, mp->ip4_addr, 4); + + rv = dslite_set_b4_ip6_addr (dm, &ip6_addr); + if (rv == 0) + rv = dslite_set_b4_ip4_addr (dm, &ip4_addr); + + REPLY_MACRO (VL_API_DSLITE_SET_B4_ADDR_REPLY); +} + +static void * +vl_api_dslite_set_b4_addr_t_print (vl_api_dslite_set_b4_addr_t * mp, + void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: dslite_set_b4_addr "); + s = format (s, "ip6_addr %U ip4_addr %U\n", + format_ip6_address, mp->ip6_addr, + format_ip6_address, mp->ip4_addr); + + FINISH; +} + +static void +vl_api_dslite_get_b4_addr_t_handler (vl_api_dslite_get_b4_addr_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_dslite_get_b4_addr_reply_t *rmp; + dslite_main_t *dm = &dslite_main; + int rv = 0; + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_DSLITE_GET_AFTR_ADDR_REPLY, + ({ + memcpy (rmp->ip4_addr, &dm->b4_ip4_addr.as_u8, 4); + memcpy (rmp->ip6_addr, &dm->b4_ip6_addr.as_u8, 16); + })) + /* *INDENT-ON* */ +} + +static void * +vl_api_dslite_get_b4_addr_t_print (vl_api_dslite_get_b4_addr_t * mp, + void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: dslite_get_b4_addr"); + + FINISH; +} + static void vl_api_dslite_add_del_pool_addr_range_t_handler (vl_api_dslite_add_del_pool_addr_range_t * mp) @@ -2507,7 +2601,10 @@ _(NAT64_ADD_DEL_PREFIX, nat64_add_del_prefix) \ _(NAT64_PREFIX_DUMP, nat64_prefix_dump) \ _(NAT64_ADD_DEL_INTERFACE_ADDR, nat64_add_del_interface_addr) \ _(DSLITE_ADD_DEL_POOL_ADDR_RANGE, dslite_add_del_pool_addr_range) \ -_(DSLITE_SET_AFTR_ADDR, dslite_set_aftr_addr) +_(DSLITE_SET_AFTR_ADDR, dslite_set_aftr_addr) \ +_(DSLITE_GET_AFTR_ADDR, dslite_get_aftr_addr) \ +_(DSLITE_SET_B4_ADDR, dslite_set_b4_addr) \ +_(DSLITE_GET_B4_ADDR, dslite_get_b4_addr) /* Set up the API message handling tables */ static clib_error_t * diff --git a/test/test_nat.py b/test/test_nat.py index 7194704046d..5f6334ebb39 100644 --- a/test/test_nat.py +++ b/test/test_nat.py @@ -5608,5 +5608,109 @@ class TestDSlite(MethodHolder): self.vapi.cli("show dslite aftr-tunnel-endpoint-address")) self.logger.info(self.vapi.cli("show dslite sessions")) + +class TestDSliteCE(MethodHolder): + """ DS-Lite CE Test Cases """ + + @classmethod + def setUpConstants(cls): + super(TestDSliteCE, cls).setUpConstants() + cls.vpp_cmdline.extend(["nat", "{", "dslite ce", "}"]) + + @classmethod + def setUpClass(cls): + super(TestDSliteCE, cls).setUpClass() + + try: + cls.create_pg_interfaces(range(2)) + cls.pg0.admin_up() + cls.pg0.config_ip4() + cls.pg0.resolve_arp() + cls.pg1.admin_up() + cls.pg1.config_ip6() + cls.pg1.generate_remote_hosts(1) + cls.pg1.configure_ipv6_neighbors() + + except Exception: + super(TestDSliteCE, cls).tearDownClass() + raise + + def test_dslite_ce(self): + """ Test DS-Lite CE """ + + b4_ip4 = '192.0.0.2' + b4_ip4_n = socket.inet_pton(socket.AF_INET, b4_ip4) + b4_ip6 = '2001:db8:62aa::375e:f4c1:1' + b4_ip6_n = socket.inet_pton(socket.AF_INET6, b4_ip6) + self.vapi.dslite_set_b4_addr(b4_ip6_n, b4_ip4_n) + + aftr_ip4 = '192.0.0.1' + aftr_ip4_n = socket.inet_pton(socket.AF_INET, aftr_ip4) + aftr_ip6 = '2001:db8:85a3::8a2e:370:1' + aftr_ip6_n = socket.inet_pton(socket.AF_INET6, aftr_ip6) + self.vapi.dslite_set_aftr_addr(aftr_ip6_n, aftr_ip4_n) + + self.vapi.ip_add_del_route(dst_address=aftr_ip6_n, + dst_address_length=128, + next_hop_address=self.pg1.remote_ip6n, + next_hop_sw_if_index=self.pg1.sw_if_index, + is_ipv6=1) + + # UDP encapsulation + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(dst=self.pg1.remote_ip4, src=self.pg0.remote_ip4) / + UDP(sport=10000, dport=20000)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + capture = capture[0] + self.assertEqual(capture[IPv6].src, b4_ip6) + self.assertEqual(capture[IPv6].dst, aftr_ip6) + self.assertEqual(capture[IP].src, self.pg0.remote_ip4) + self.assertEqual(capture[IP].dst, self.pg1.remote_ip4) + self.assertEqual(capture[UDP].sport, 10000) + self.assertEqual(capture[UDP].dport, 20000) + self.check_ip_checksum(capture) + + # UDP decapsulation + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IPv6(dst=b4_ip6, src=aftr_ip6) / + IP(dst=self.pg0.remote_ip4, src=self.pg1.remote_ip4) / + UDP(sport=20000, dport=10000)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + capture = capture[0] + self.assertFalse(capture.haslayer(IPv6)) + self.assertEqual(capture[IP].src, self.pg1.remote_ip4) + self.assertEqual(capture[IP].dst, self.pg0.remote_ip4) + self.assertEqual(capture[UDP].sport, 20000) + self.assertEqual(capture[UDP].dport, 10000) + self.check_ip_checksum(capture) + + # ping DS-Lite B4 tunnel endpoint address + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IPv6(src=self.pg1.remote_hosts[0].ip6, dst=b4_ip6) / + ICMPv6EchoRequest()) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + self.assertEqual(1, len(capture)) + capture = capture[0] + self.assertEqual(capture[IPv6].src, b4_ip6) + self.assertEqual(capture[IPv6].dst, self.pg1.remote_hosts[0].ip6) + self.assertTrue(capture.haslayer(ICMPv6EchoReply)) + + def tearDown(self): + super(TestDSliteCE, self).tearDown() + if not self.vpp_dead: + self.logger.info( + self.vapi.cli("show dslite aftr-tunnel-endpoint-address")) + self.logger.info( + self.vapi.cli("show dslite b4-tunnel-endpoint-address")) + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index acd05a7a26d..7a6d11f75ae 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -1818,6 +1818,17 @@ class VppPapiProvider(object): {'ip4_addr': ip4, 'ip6_addr': ip6}) + def dslite_set_b4_addr(self, ip6, ip4): + """Set DS-Lite B4 IPv6 address + + :param ip4: IPv4 address + :param ip6: IPv6 address + """ + return self.api( + self.papi.dslite_set_b4_addr, + {'ip4_addr': ip4, + 'ip6_addr': ip6}) + def dslite_add_del_pool_addr_range( self, start_addr, -- 2.16.6