diff options
author | Ole Troan <ot@cisco.com> | 2019-12-19 11:55:54 +0100 |
---|---|---|
committer | Andrew Yourtchenko <ayourtch@gmail.com> | 2020-02-05 15:45:30 +0000 |
commit | 2c6639c695aebf0cd5ac74ad31fd331547fa0126 (patch) | |
tree | dd962dda017462107eca0b838c159ea1e19f7b8a /src/plugins/nat/dslite | |
parent | 72e31bc2d9b910147c09e1c329713fccc873a018 (diff) |
nat: move dslite to separate sub-plugin
Type: refactor
Change-Id: If3d9f16f3a06c10b354f1eef674e8db5f3c44de7
Signed-off-by: Ole Troan <ot@cisco.com>
Diffstat (limited to 'src/plugins/nat/dslite')
-rw-r--r-- | src/plugins/nat/dslite/dslite.api | 134 | ||||
-rw-r--r-- | src/plugins/nat/dslite/dslite.c | 265 | ||||
-rw-r--r-- | src/plugins/nat/dslite/dslite.h | 166 | ||||
-rw-r--r-- | src/plugins/nat/dslite/dslite_api.c | 177 | ||||
-rw-r--r-- | src/plugins/nat/dslite/dslite_ce_decap.c | 142 | ||||
-rw-r--r-- | src/plugins/nat/dslite/dslite_ce_encap.c | 134 | ||||
-rw-r--r-- | src/plugins/nat/dslite/dslite_cli.c | 404 | ||||
-rw-r--r-- | src/plugins/nat/dslite/dslite_dpo.c | 130 | ||||
-rw-r--r-- | src/plugins/nat/dslite/dslite_dpo.h | 38 | ||||
-rw-r--r-- | src/plugins/nat/dslite/dslite_in2out.c | 515 | ||||
-rw-r--r-- | src/plugins/nat/dslite/dslite_out2in.c | 294 |
11 files changed, 2399 insertions, 0 deletions
diff --git a/src/plugins/nat/dslite/dslite.api b/src/plugins/nat/dslite/dslite.api new file mode 100644 index 00000000000..b5f8c5ba833 --- /dev/null +++ b/src/plugins/nat/dslite/dslite.api @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2019 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. + */ + +option version = "1.0.0"; +import "vnet/ip/ip_types.api"; +import "vnet/interface_types.api"; + +/** + * @file nat.api + * @brief VPP control-plane API messages. + * + * This file defines VPP control-plane API messages which are generally + * called through a shared memory interface. + */ + + +/* + * DS-Lite APIs + */ + +/** \brief Add/delete address range to DS-Lite pool + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param start_addr - start IPv4 address of the range + @param end_addr - end IPv4 address of the range + @param is_add - true if add, false if delete +*/ +autoreply define dslite_add_del_pool_addr_range { + u32 client_index; + u32 context; + vl_api_ip4_address_t start_addr; + vl_api_ip4_address_t end_addr; + bool is_add; +}; + +/** \brief Dump DS-Lite addresses + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define dslite_address_dump { + u32 client_index; + u32 context; +}; + +/** \brief DS-Lite address details response + @param context - sender context, to match reply w/ request + @param ip_address - IPv4 address +*/ +define dslite_address_details { + u32 context; + vl_api_ip4_address_t ip_address; +}; + +/** \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 - IPv6 address +*/ +autoreply define dslite_set_aftr_addr { + u32 client_index; + u32 context; + vl_api_ip4_address_t ip4_addr; + vl_api_ip6_address_t ip6_addr; +}; + +/** \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 - IPv6 address +*/ +define dslite_get_aftr_addr_reply { + u32 context; + i32 retval; + vl_api_ip4_address_t ip4_addr; + vl_api_ip6_address_t ip6_addr; +}; + +/** \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 - IPv6 address +*/ +autoreply define dslite_set_b4_addr { + u32 client_index; + u32 context; + vl_api_ip4_address_t ip4_addr; + vl_api_ip6_address_t ip6_addr; +}; + +/** \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 - IPv6 address +*/ +define dslite_get_b4_addr_reply { + u32 context; + i32 retval; + vl_api_ip4_address_t ip4_addr; + vl_api_ip6_address_t ip6_addr; +}; diff --git a/src/plugins/nat/dslite/dslite.c b/src/plugins/nat/dslite/dslite.c new file mode 100644 index 00000000000..e03dd0c8431 --- /dev/null +++ b/src/plugins/nat/dslite/dslite.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2017 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 <vnet/plugin/plugin.h> +#include <nat/dslite/dslite.h> +#include <nat/dslite/dslite_dpo.h> +#include <vnet/fib/fib_table.h> +#include <vpp/app/version.h> + +dslite_main_t dslite_main; +fib_source_t nat_fib_src_hi; + +clib_error_t *dslite_api_hookup (vlib_main_t * vm); + +void +add_del_dslite_pool_addr_cb (ip4_address_t addr, u8 is_add, void *opaque); + +static clib_error_t * +dslite_init (vlib_main_t * vm) +{ + dslite_main_t *dm = &dslite_main; + vlib_thread_registration_t *tr; + vlib_thread_main_t *tm = vlib_get_thread_main (); + uword *p; + vlib_node_t *node; + dslite_per_thread_data_t *td; + u32 translation_buckets = 1024; + u32 translation_memory_size = 128 << 20; + u32 b4_buckets = 128; + u32 b4_memory_size = 64 << 20; + + node = vlib_get_node_by_name (vm, (u8 *) "dslite-in2out"); + dm->dslite_in2out_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "dslite-in2out-slowpath"); + dm->dslite_in2out_slowpath_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "dslite-out2in"); + dm->dslite_out2in_node_index = node->index; + + dm->first_worker_index = 0; + dm->num_workers = 0; + + // init nat address pool + dm->pool.add_del_pool_addr_cb = add_del_dslite_pool_addr_cb; + dm->pool.alloc_addr_and_port_cb = nat_alloc_ip4_addr_and_port_cb_default; + + p = hash_get_mem (tm->thread_registrations_by_name, "workers"); + if (p) + { + tr = (vlib_thread_registration_t *) p[0]; + if (tr) + { + dm->num_workers = tr->count; + dm->first_worker_index = tr->first_index; + } + } + + if (dm->num_workers) + dm->port_per_thread = (0xffff - 1024) / dm->num_workers; + else + dm->port_per_thread = 0xffff - 1024; + + vec_validate (dm->per_thread_data, tm->n_vlib_mains - 1); + + /* *INDENT-OFF* */ + vec_foreach (td, dm->per_thread_data) + { + clib_bihash_init_24_8 (&td->in2out, "in2out", translation_buckets, + translation_memory_size); + + clib_bihash_init_8_8 (&td->out2in, "out2in", translation_buckets, + translation_memory_size); + + clib_bihash_init_16_8 (&td->b4_hash, "b4s", b4_buckets, b4_memory_size); + } + /* *INDENT-ON* */ + + dm->is_ce = 0; + + /* Init counters */ + dm->total_b4s.name = "total-b4s"; + dm->total_b4s.stat_segment_name = "/dslite/total-b4s"; + vlib_validate_simple_counter (&dm->total_b4s, 0); + vlib_zero_simple_counter (&dm->total_b4s, 0); + dm->total_sessions.name = "total-sessions"; + dm->total_sessions.stat_segment_name = "/dslite/total-sessions"; + vlib_validate_simple_counter (&dm->total_sessions, 0); + vlib_zero_simple_counter (&dm->total_sessions, 0); + + dslite_dpo_module_init (); + + nat_fib_src_hi = fib_source_allocate ("dslite-hi", + FIB_SOURCE_PRIORITY_HI, + FIB_SOURCE_BH_SIMPLE); + + return dslite_api_hookup (vm); +} + +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 = DPO_INVALID; + + 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, nat_fib_src_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, nat_fib_src_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, nat_fib_src_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; +} + +void +add_del_dslite_pool_addr_cb (ip4_address_t addr, u8 is_add, void *opaque) +{ + dpo_id_t dpo_v4 = DPO_INVALID; + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP4, + .fp_len = 32, + .fp_addr.ip4.as_u32 = addr.as_u32, + }; + + if (is_add) + { + dslite_dpo_create (DPO_PROTO_IP4, 0, &dpo_v4); + fib_table_entry_special_dpo_add (0, &pfx, nat_fib_src_hi, + FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v4); + dpo_reset (&dpo_v4); + } + else + { + fib_table_entry_special_remove (0, &pfx, nat_fib_src_hi); + } +} + +u8 * +format_dslite_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_trace_t *t = va_arg (*args, dslite_trace_t *); + + s = + format (s, "next index %d, session %d", t->next_index, t->session_index); + + 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; +} + +VLIB_INIT_FUNCTION (dslite_init); + +VLIB_PLUGIN_REGISTER () = +{ +.version = VPP_BUILD_VER,.description = "Dual-Stack Lite",}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/dslite/dslite.h b/src/plugins/nat/dslite/dslite.h new file mode 100644 index 00000000000..d512cf47c9b --- /dev/null +++ b/src/plugins/nat/dslite/dslite.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2017 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. + */ +#ifndef __included_dslite_h__ +#define __included_dslite_h__ + +#include <vppinfra/bihash_8_8.h> +#include <vppinfra/bihash_16_8.h> +#include <vppinfra/bihash_24_8.h> +#include <nat/lib/alloc.h> +#include <nat/nat.h> +#include <nat/nat_inlines.h> + +typedef struct +{ + union + { + struct + { + ip6_address_t softwire_id; + ip4_address_t addr; + u16 port; + u8 proto; + u8 pad; + }; + u64 as_u64[3]; + }; +} dslite_session_key_t; + +/* *INDENT-OFF* */ +typedef CLIB_PACKED (struct +{ + snat_session_key_t out2in; + dslite_session_key_t in2out; + u32 per_b4_index; + u32 per_b4_list_head_index; + f64 last_heard; + u64 total_bytes; + u32 total_pkts; +}) dslite_session_t; +/* *INDENT-ON* */ + +typedef struct +{ + ip6_address_t addr; + u32 sessions_per_b4_list_head_index; + u32 nsessions; +} dslite_b4_t; + +typedef struct +{ + /* Main lookup tables */ + clib_bihash_8_8_t out2in; + clib_bihash_24_8_t in2out; + + /* Find a B4 */ + clib_bihash_16_8_t b4_hash; + + /* B4 pool */ + dslite_b4_t *b4s; + + /* Session pool */ + dslite_session_t *sessions; + + /* Pool of doubly-linked list elements */ + dlist_elt_t *list_pool; +} dslite_per_thread_data_t; + +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; + u32 num_workers; + u32 first_worker_index; + u16 port_per_thread; + + /* nat address pool */ + nat_ip4_pool_t pool; + + /* counters/gauges */ + vlib_simple_counter_main_t total_b4s; + vlib_simple_counter_main_t total_sessions; + + /* node index */ + u32 dslite_in2out_node_index; + u32 dslite_in2out_slowpath_node_index; + u32 dslite_out2in_node_index; + + /* If set then the DSLite component behaves as CPE/B4 + * otherwise it behaves as AFTR */ + u8 is_ce; + + u16 msg_id_base; +} dslite_main_t; + +typedef struct +{ + u32 next_index; + 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") \ +_(UNSUPPORTED_PROTOCOL, "unsupported protocol") \ +_(BAD_ICMP_TYPE, "unsupported icmp type") \ +_(UNKNOWN, "unknown") + +typedef enum +{ +#define _(sym,str) DSLITE_ERROR_##sym, + foreach_dslite_error +#undef _ + DSLITE_N_ERROR, +} dslite_error_t; + +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_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__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/dslite/dslite_api.c b/src/plugins/nat/dslite/dslite_api.c new file mode 100644 index 00000000000..420e8212ad9 --- /dev/null +++ b/src/plugins/nat/dslite/dslite_api.c @@ -0,0 +1,177 @@ +/* + *------------------------------------------------------------------ + * dslite_api.c - DS-Lite API + * + * Copyright (c) 2019 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 <vnet/ip/ip_types_api.h> +#include <nat/dslite/dslite.h> +#include <nat/dslite/dslite.api_enum.h> +#include <nat/dslite/dslite.api_types.h> +#include <vnet/ip/ip.h> +#include <vnet/fib/fib_table.h> +#include <vlibmemory/api.h> + +#define REPLY_MSG_ID_BASE dm->msg_id_base +#include <vlibapi/api_helper_macros.h> + +static void +vl_api_dslite_set_aftr_addr_t_handler (vl_api_dslite_set_aftr_addr_t * mp) +{ + vl_api_dslite_set_aftr_addr_reply_t *rmp; + 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); +} + +static void +vl_api_dslite_get_aftr_addr_t_handler (vl_api_dslite_get_aftr_addr_t * mp) +{ + 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_set_b4_addr_t_handler (vl_api_dslite_set_b4_addr_t * mp) +{ + vl_api_dslite_set_b4_addr_reply_t *rmp; + 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_get_b4_addr_t_handler (vl_api_dslite_get_b4_addr_t * mp) +{ + 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_B4_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_add_del_pool_addr_range_t_handler + (vl_api_dslite_add_del_pool_addr_range_t * mp) +{ + vl_api_dslite_add_del_pool_addr_range_reply_t *rmp; + dslite_main_t *dm = &dslite_main; + int rv = 0; + ip4_address_t this_addr; + u32 start_host_order, end_host_order; + int count; + u32 *tmp; + + tmp = (u32 *) mp->start_addr; + start_host_order = clib_host_to_net_u32 (tmp[0]); + tmp = (u32 *) mp->end_addr; + end_host_order = clib_host_to_net_u32 (tmp[0]); + + // TODO: + // end_host_order < start_host_order + + count = (end_host_order - start_host_order) + 1; + memcpy (&this_addr.as_u8, mp->start_addr, 4); + + rv = nat_add_del_ip4_pool_addrs (&dm->pool, this_addr, count, mp->is_add, 0); + + REPLY_MACRO (VL_API_DSLITE_ADD_DEL_POOL_ADDR_RANGE_REPLY); +} + +static void +send_dslite_address_details (nat_ip4_pool_addr_t * a, + vl_api_registration_t * reg, u32 context) +{ + dslite_main_t *dm = &dslite_main; + vl_api_dslite_address_details_t *rmp; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + + clib_memset (rmp, 0, sizeof (*rmp)); + + rmp->_vl_msg_id = ntohs (VL_API_DSLITE_ADDRESS_DETAILS + dm->msg_id_base); + clib_memcpy (rmp->ip_address, &(a->addr), 4); + rmp->context = context; + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_dslite_address_dump_t_handler (vl_api_dslite_address_dump_t * mp) +{ + vl_api_registration_t *reg; + dslite_main_t *dm = &dslite_main; + nat_ip4_pool_addr_t *a; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + /* *INDENT-OFF* */ + vec_foreach (a, dm->pool.pool_addr) + { + send_dslite_address_details (a, reg, mp->context); + } + /* *INDENT-ON* */ +} + +/* API definitions */ +#include <vnet/format_fns.h> +#include <nat/dslite/dslite.api.c> + +/* Set up the API message handling tables */ +clib_error_t * +dslite_api_hookup (vlib_main_t * vm) +{ + dslite_main_t *dm = &dslite_main; + + dm->msg_id_base = setup_message_id_table (); + return 0; +} diff --git a/src/plugins/nat/dslite/dslite_ce_decap.c b/src/plugins/nat/dslite/dslite_ce_decap.c new file mode 100644 index 00000000000..3d6ca992e45 --- /dev/null +++ b/src/plugins/nat/dslite/dslite_ce_decap.c @@ -0,0 +1,142 @@ +/* + * 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 <nat/dslite/dslite.h> +#include <nat/nat_inlines.h> + +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 _ +}; + +VLIB_NODE_FN (dslite_ce_decap_node) (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) = { + .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* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/dslite/dslite_ce_encap.c b/src/plugins/nat/dslite/dslite_ce_encap.c new file mode 100644 index 00000000000..8cec5439243 --- /dev/null +++ b/src/plugins/nat/dslite/dslite_ce_encap.c @@ -0,0 +1,134 @@ +/* + * 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 <nat/dslite/dslite.h> +#include <nat/nat_inlines.h> + +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 _ +}; + +VLIB_NODE_FN (dslite_ce_encap_node) (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) = { + .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* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/dslite/dslite_cli.c b/src/plugins/nat/dslite/dslite_cli.c new file mode 100644 index 00000000000..d5c0ca6498b --- /dev/null +++ b/src/plugins/nat/dslite/dslite_cli.c @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2017 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 <nat/dslite/dslite.h> + +static clib_error_t * +dslite_add_del_pool_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; + ip4_address_t start_addr, end_addr, this_addr; + u32 start_host_order, end_host_order; + int count, rv; + u8 is_add = 1; + 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 - %U", + unformat_ip4_address, &start_addr, + unformat_ip4_address, &end_addr)) + ; + else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr)) + end_addr = start_addr; + else if (unformat (line_input, "del")) + is_add = 0; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + start_host_order = clib_host_to_net_u32 (start_addr.as_u32); + end_host_order = clib_host_to_net_u32 (end_addr.as_u32); + + if (end_host_order < start_host_order) + { + error = clib_error_return (0, "end address less than start address"); + goto done; + } + + count = (end_host_order - start_host_order) + 1; + this_addr = start_addr; + + rv = nat_add_del_ip4_pool_addrs (&dm->pool, this_addr, count, is_add, 0); + + switch (rv) + { + case VNET_API_ERROR_NO_SUCH_ENTRY: + error = + clib_error_return (0, "DS-Lite pool address %U not exist.", + format_ip4_address, &this_addr); + break; + case VNET_API_ERROR_VALUE_EXIST: + error = + clib_error_return (0, "DS-Lite pool address %U exist.", + format_ip4_address, &this_addr); + break; + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +dslite_show_pool_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + dslite_main_t *dm = &dslite_main; + nat_ip4_pool_addr_t *a; + + vlib_cli_output (vm, "DS-Lite pool:"); + + /* *INDENT-OFF* */ + vec_foreach (a, dm->pool.pool_addr) + { + vlib_cli_output (vm, "%U", format_ip4_address, &a->addr); + } + /* *INDENT-ON* */ + return 0; +} + +static clib_error_t * +dslite_set_aftr_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_aftr_ip6_addr (dm, &ip6_addr); + + if (rv) + error = + clib_error_return (0, + "Set DS-Lite AFTR tunnel endpoint address failed."); + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +dslite_show_aftr_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->aftr_ip6_addr); + 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) +{ + dslite_session_t *session = va_arg (*args, dslite_session_t *); + u32 indent = format_get_indent (s); + + s = format (s, "%Uin %U:%u out %U:%u protocol %U\n", + format_white_space, indent + 2, + format_ip4_address, &session->in2out.addr, + clib_net_to_host_u16 (session->in2out.port), + format_ip4_address, &session->out2in.addr, + clib_net_to_host_u16 (session->out2in.port), + format_snat_protocol, session->in2out.proto); + s = format (s, "%Utotal pkts %d, total bytes %lld\n", + format_white_space, indent + 4, + session->total_pkts, session->total_bytes); + return s; +} + +static u8 * +format_dslite_b4 (u8 * s, va_list * args) +{ + dslite_per_thread_data_t *td = va_arg (*args, dslite_per_thread_data_t *); + dslite_b4_t *b4 = va_arg (*args, dslite_b4_t *); + dlist_elt_t *head, *elt; + u32 elt_index, head_index; + u32 session_index; + dslite_session_t *session; + + s = + format (s, "B4 %U %d sessions\n", format_ip6_address, &b4->addr, + b4->nsessions); + + if (b4->nsessions == 0) + return s; + + head_index = b4->sessions_per_b4_list_head_index; + head = pool_elt_at_index (td->list_pool, head_index); + elt_index = head->next; + elt = pool_elt_at_index (td->list_pool, elt_index); + session_index = elt->value; + while (session_index != ~0) + { + session = pool_elt_at_index (td->sessions, session_index); + s = format (s, "%U", format_dslite_session, session); + elt_index = elt->next; + elt = pool_elt_at_index (td->list_pool, elt_index); + session_index = elt->value; + } + + return s; +} + +static clib_error_t * +dslite_show_sessions_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + dslite_main_t *dm = &dslite_main; + dslite_per_thread_data_t *td; + dslite_b4_t *b4; + + /* *INDENT-OFF* */ + vec_foreach (td, dm->per_thread_data) + { + pool_foreach (b4, td->b4s, + ({ + vlib_cli_output (vm, "%U", format_dslite_b4, td, b4); + })); + } + /* *INDENT-ON* */ + + return 0; +} + +/* *INDENT-OFF* */ + +/*? + * @cliexpar + * @cliexstart{dslite add pool address} + * Add/delete DS-Lite pool address for AFTR element. + * To add DS-Lite pool address use: + * vpp# dslite add pool address 10.1.1.3 + * To add DS-Lite pool address range use: + * vpp# dslite add pool address 10.1.1.5 - 10.1.1.7 + * @cliexend +?*/ +VLIB_CLI_COMMAND (dslite_add_pool_address_command, static) = { + .path = "dslite add pool address", + .short_help = "dslite add pool address <ip4-range-start> [- <ip4-range-end>] " + " [del]", + .function = dslite_add_del_pool_addr_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show dslite pool} + * Show DS-lite pool addresses. + * vpp# show dslite pool + * DS-Lite pool: + * 10.0.0.3 + * 10.0.0.5 + * 10.0.0.6 + * 10.0.0.7 + * @cliexend +?*/ +VLIB_CLI_COMMAND (show_dslite_pool_command, static) = { + .path = "show dslite pool", + .short_help = "show dslite pool", + .function = dslite_show_pool_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{dslite set aftr-tunnel-endpoint-address} + * Set IPv6 tunnel endpoint address of the AFTR element. + * To set AFTR tunnel endpoint address use: + * vpp# dslite set aftr-tunnel-endpoint-address 2001:db8:85a3::8a2e:370:1 + * @cliexend +?*/ +VLIB_CLI_COMMAND (dslite_set_aftr_tunnel_addr, static) = { + .path = "dslite set aftr-tunnel-endpoint-address", + .short_help = "dslite set aftr-tunnel-endpoint-address <ip6>", + .function = dslite_set_aftr_tunnel_addr_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show dslite aftr-tunnel-endpoint-address} + * Show IPv6 tunnel endpoint address of the AFTR element. + * vpp# show dslite aftr-tunnel-endpoint-address + * 2001:db8:85a3::8a2e:370:1 + * @cliexend +?*/ +VLIB_CLI_COMMAND (dslite_show_aftr_ip6_addr, static) = { + .path = "show dslite aftr-tunnel-endpoint-address", + .short_help = "show dslite aftr-tunnel-endpoint-address", + .function = dslite_show_aftr_ip6_addr_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{dslite set b4-tunnel-endpoint-address} + * Set IPv6 tunnel endpoint address of the B4 element. + * To set B4 tunnel endpoint address use: + * vpp# dslite set b4-tunnel-endpoint-address 2001:db8:62aa::375e:f4c1:1 + * @cliexend +?*/ +VLIB_CLI_COMMAND (dslite_set_b4_tunnel_addr, static) = { + .path = "dslite set b4-tunnel-endpoint-address", + .short_help = "dslite set b4-tunnel-endpoint-address <ip6>", + .function = dslite_set_b4_tunnel_addr_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show dslite b4-tunnel-endpoint-address} + * Show IPv6 tunnel endpoint address of the B4 element. + * vpp# show dslite b4-tunnel-endpoint-address + * 2001:db8:62aa::375e:f4c1:1 + * @cliexend +?*/ +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, +}; + +/*? + * @cliexpar + * @cliexstart{show dslite sessions} + * Show DS-Lite sessions. + * vpp# show dslite sessions + * B4 fd01:2::2 1 sessions + * in 192.168.1.1:20000 out 10.0.0.3:16253 protocol udp + * total pkts 2, total bytes 136 + * B4 fd01:2::3 2 sessions + * in 192.168.1.1:20001 out 10.0.0.3:18995 protocol tcp + * total pkts 2, total bytes 160 + * in 192.168.1.1:4000 out 10.0.0.3:53893 protocol icmp + * total pkts 2, total bytes 136 + * @cliexend +?*/ +VLIB_CLI_COMMAND (dslite_show_sessions, static) = { + .path = "show dslite sessions", + .short_help = "show dslite sessions", + .function = dslite_show_sessions_command_fn, +}; + +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/dslite/dslite_dpo.c b/src/plugins/nat/dslite/dslite_dpo.c new file mode 100644 index 00000000000..009b5536289 --- /dev/null +++ b/src/plugins/nat/dslite/dslite_dpo.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2017 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 <vnet/ip/ip.h> +#include <nat/dslite/dslite_dpo.h> + +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) +{ + 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) +{ + index_t index = va_arg (*args, index_t); + CLIB_UNUSED (u32 indent) = va_arg (*args, u32); + + 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) +{ +} + +static void +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, +}; + +const static char *const dslite_ip6_nodes[] = { + "dslite-in2out", + 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); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/dslite/dslite_dpo.h b/src/plugins/nat/dslite/dslite_dpo.h new file mode 100644 index 00000000000..53d37b27fb4 --- /dev/null +++ b/src/plugins/nat/dslite/dslite_dpo.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef __included_dslite_dpo_h__ +#define __included_dslite_dpo_h__ + +#include <vnet/vnet.h> +#include <vnet/dpo/dpo.h> + +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); + +#endif /* __included_dslite_dpo_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/dslite/dslite_in2out.c b/src/plugins/nat/dslite/dslite_in2out.c new file mode 100644 index 00000000000..d1ac17509c6 --- /dev/null +++ b/src/plugins/nat/dslite/dslite_in2out.c @@ -0,0 +1,515 @@ +/* + * Copyright (c) 2017 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 <nat/dslite/dslite.h> +#include <nat/nat_inlines.h> +#include <nat/nat_syslog.h> + +typedef enum +{ + DSLITE_IN2OUT_NEXT_IP4_LOOKUP, + DSLITE_IN2OUT_NEXT_IP6_ICMP, + DSLITE_IN2OUT_NEXT_DROP, + DSLITE_IN2OUT_NEXT_SLOWPATH, + DSLITE_IN2OUT_N_NEXT, +} dslite_in2out_next_t; + +static char *dslite_in2out_error_strings[] = { +#define _(sym,string) string, + foreach_dslite_error +#undef _ +}; + +static u32 +slow_path (dslite_main_t * dm, dslite_session_key_t * in2out_key, + dslite_session_t ** sp, u32 next, u8 * error, u32 thread_index) +{ + dslite_b4_t *b4; + clib_bihash_kv_16_8_t b4_kv, b4_value; + clib_bihash_kv_24_8_t in2out_kv; + clib_bihash_kv_8_8_t out2in_kv; + dlist_elt_t *head_elt, *oldest_elt, *elt; + u32 oldest_index; + dslite_session_t *s; + snat_session_key_t out2in_key; + nat_ip4_addr_port_t addr_port; + u32 b4_index; + + out2in_key.protocol = in2out_key->proto; + out2in_key.fib_index = 0; + + b4_kv.key[0] = in2out_key->softwire_id.as_u64[0]; + b4_kv.key[1] = in2out_key->softwire_id.as_u64[1]; + + if (clib_bihash_search_16_8 + (&dm->per_thread_data[thread_index].b4_hash, &b4_kv, &b4_value)) + { + pool_get (dm->per_thread_data[thread_index].b4s, b4); + clib_memset (b4, 0, sizeof (*b4)); + b4->addr.as_u64[0] = in2out_key->softwire_id.as_u64[0]; + b4->addr.as_u64[1] = in2out_key->softwire_id.as_u64[1]; + + pool_get (dm->per_thread_data[thread_index].list_pool, head_elt); + b4->sessions_per_b4_list_head_index = + head_elt - dm->per_thread_data[thread_index].list_pool; + clib_dlist_init (dm->per_thread_data[thread_index].list_pool, + b4->sessions_per_b4_list_head_index); + + b4_index = b4_kv.value = b4 - dm->per_thread_data[thread_index].b4s; + clib_bihash_add_del_16_8 (&dm->per_thread_data[thread_index].b4_hash, + &b4_kv, 1); + + vlib_set_simple_counter (&dm->total_b4s, thread_index, 0, + pool_elts (dm-> + per_thread_data[thread_index].b4s)); + } + else + { + b4_index = b4_value.value; + b4 = + pool_elt_at_index (dm->per_thread_data[thread_index].b4s, + b4_value.value); + } + + //TODO configurable quota + if (b4->nsessions >= 1000) + { + oldest_index = + clib_dlist_remove_head (dm->per_thread_data[thread_index].list_pool, + b4->sessions_per_b4_list_head_index); + ASSERT (oldest_index != ~0); + clib_dlist_addtail (dm->per_thread_data[thread_index].list_pool, + b4->sessions_per_b4_list_head_index, oldest_index); + oldest_elt = + pool_elt_at_index (dm->per_thread_data[thread_index].list_pool, + oldest_index); + s = + pool_elt_at_index (dm->per_thread_data[thread_index].sessions, + oldest_elt->value); + + in2out_kv.key[0] = s->in2out.as_u64[0]; + in2out_kv.key[1] = s->in2out.as_u64[1]; + in2out_kv.key[2] = s->in2out.as_u64[2]; + clib_bihash_add_del_24_8 (&dm->per_thread_data[thread_index].in2out, + &in2out_kv, 0); + out2in_kv.key = s->out2in.as_u64; + clib_bihash_add_del_8_8 (&dm->per_thread_data[thread_index].out2in, + &out2in_kv, 0); + + addr_port.addr.as_u32 = s->out2in.addr.as_u32; + addr_port.port = s->out2in.port; + + nat_free_ip4_addr_and_port (&dm->pool, thread_index, + s->out2in.protocol, &addr_port); + + nat_syslog_dslite_apmdel (b4_index, &s->in2out.softwire_id, + &s->in2out.addr, s->in2out.port, + &s->out2in.addr, s->out2in.port, + s->in2out.proto); + + if (nat_alloc_ip4_addr_and_port + (&dm->pool, 0, thread_index, thread_index, + dm->port_per_thread, out2in_key.protocol, &addr_port)) + ASSERT (0); + + out2in_key.addr.as_u32 = addr_port.addr.as_u32; + out2in_key.port = addr_port.port; + } + else + { + if (nat_alloc_ip4_addr_and_port + (&dm->pool, 0, thread_index, thread_index, + dm->port_per_thread, out2in_key.protocol, &addr_port)) + { + *error = DSLITE_ERROR_OUT_OF_PORTS; + return DSLITE_IN2OUT_NEXT_DROP; + } + + out2in_key.addr.as_u32 = addr_port.addr.as_u32; + out2in_key.port = addr_port.port; + + pool_get (dm->per_thread_data[thread_index].sessions, s); + clib_memset (s, 0, sizeof (*s)); + b4->nsessions++; + + pool_get (dm->per_thread_data[thread_index].list_pool, elt); + clib_dlist_init (dm->per_thread_data[thread_index].list_pool, + elt - dm->per_thread_data[thread_index].list_pool); + elt->value = s - dm->per_thread_data[thread_index].sessions; + s->per_b4_index = elt - dm->per_thread_data[thread_index].list_pool; + s->per_b4_list_head_index = b4->sessions_per_b4_list_head_index; + clib_dlist_addtail (dm->per_thread_data[thread_index].list_pool, + s->per_b4_list_head_index, + elt - dm->per_thread_data[thread_index].list_pool); + + vlib_set_simple_counter (&dm->total_sessions, thread_index, 0, + pool_elts (dm->per_thread_data + [thread_index].sessions)); + } + + s->in2out = *in2out_key; + s->out2in = out2in_key; + *sp = s; + in2out_kv.key[0] = s->in2out.as_u64[0]; + in2out_kv.key[1] = s->in2out.as_u64[1]; + in2out_kv.key[2] = s->in2out.as_u64[2]; + in2out_kv.value = s - dm->per_thread_data[thread_index].sessions; + clib_bihash_add_del_24_8 (&dm->per_thread_data[thread_index].in2out, + &in2out_kv, 1); + out2in_kv.key = s->out2in.as_u64; + out2in_kv.value = s - dm->per_thread_data[thread_index].sessions; + clib_bihash_add_del_8_8 (&dm->per_thread_data[thread_index].out2in, + &out2in_kv, 1); + + nat_syslog_dslite_apmadd (b4_index, &s->in2out.softwire_id, &s->in2out.addr, + s->in2out.port, &s->out2in.addr, s->out2in.port, + s->in2out.proto); + + return next; +} + +static inline u32 +dslite_icmp_in2out (dslite_main_t * dm, ip6_header_t * ip6, + ip4_header_t * ip4, dslite_session_t ** sp, u32 next, + u8 * error, u32 thread_index) +{ + dslite_session_t *s = 0; + icmp46_header_t *icmp = ip4_next_header (ip4); + clib_bihash_kv_24_8_t kv, value; + dslite_session_key_t key; + u32 n = next; + icmp_echo_header_t *echo; + u32 new_addr, old_addr; + u16 old_id, new_id; + ip_csum_t sum; + + if (icmp_type_is_error_message (icmp->type)) + { + n = DSLITE_IN2OUT_NEXT_DROP; + *error = DSLITE_ERROR_BAD_ICMP_TYPE; + goto done; + } + + echo = (icmp_echo_header_t *) (icmp + 1); + + key.addr = ip4->src_address; + key.port = echo->identifier; + key.proto = SNAT_PROTOCOL_ICMP; + key.softwire_id.as_u64[0] = ip6->src_address.as_u64[0]; + key.softwire_id.as_u64[1] = ip6->src_address.as_u64[1]; + key.pad = 0; + kv.key[0] = key.as_u64[0]; + kv.key[1] = key.as_u64[1]; + kv.key[2] = key.as_u64[2]; + + if (clib_bihash_search_24_8 + (&dm->per_thread_data[thread_index].in2out, &kv, &value)) + { + n = slow_path (dm, &key, &s, next, error, thread_index); + if (PREDICT_FALSE (next == DSLITE_IN2OUT_NEXT_DROP)) + goto done; + } + else + { + s = + pool_elt_at_index (dm->per_thread_data[thread_index].sessions, + value.value); + } + + old_addr = ip4->src_address.as_u32; + ip4->src_address = s->out2in.addr; + new_addr = ip4->src_address.as_u32; + sum = ip4->checksum; + sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, src_address); + ip4->checksum = ip_csum_fold (sum); + + old_id = echo->identifier; + echo->identifier = new_id = s->out2in.port; + sum = icmp->checksum; + sum = ip_csum_update (sum, old_id, new_id, icmp_echo_header_t, identifier); + icmp->checksum = ip_csum_fold (sum); + +done: + *sp = s; + return n; +} + +static inline uword +dslite_in2out_node_fn_inline (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame, u8 is_slow_path) +{ + u32 n_left_from, *from, *to_next; + dslite_in2out_next_t next_index; + u32 node_index; + vlib_node_runtime_t *error_node; + u32 thread_index = vm->thread_index; + f64 now = vlib_time_now (vm); + dslite_main_t *dm = &dslite_main; + + node_index = + is_slow_path ? dm->dslite_in2out_slowpath_node_index : + dm->dslite_in2out_node_index; + + error_node = vlib_node_get_runtime (vm, node_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_IN2OUT_NEXT_IP4_LOOKUP; + ip4_header_t *ip40; + ip6_header_t *ip60; + u8 error0 = DSLITE_ERROR_IN2OUT; + u32 proto0; + dslite_session_t *s0 = 0; + clib_bihash_kv_24_8_t kv0, value0; + dslite_session_key_t key0; + udp_header_t *udp0; + tcp_header_t *tcp0; + ip_csum_t sum0; + u32 new_addr0, old_addr0; + u16 old_port0, new_port0; + + /* 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_IN2OUT_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_IN2OUT_NEXT_DROP; + goto trace0; + } + + udp0 = ip4_next_header (ip40); + tcp0 = (tcp_header_t *) udp0; + + if (is_slow_path) + { + if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = + dslite_icmp_in2out (dm, ip60, ip40, &s0, next0, &error0, + thread_index); + if (PREDICT_FALSE (next0 == DSLITE_IN2OUT_NEXT_DROP)) + goto trace0; + + goto accounting0; + } + } + else + { + if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = DSLITE_IN2OUT_NEXT_SLOWPATH; + goto trace0; + } + } + + key0.addr = ip40->src_address; + key0.port = udp0->src_port; + key0.proto = proto0; + key0.softwire_id.as_u64[0] = ip60->src_address.as_u64[0]; + key0.softwire_id.as_u64[1] = ip60->src_address.as_u64[1]; + key0.pad = 0; + kv0.key[0] = key0.as_u64[0]; + kv0.key[1] = key0.as_u64[1]; + kv0.key[2] = key0.as_u64[2]; + + if (clib_bihash_search_24_8 + (&dm->per_thread_data[thread_index].in2out, &kv0, &value0)) + { + if (is_slow_path) + { + next0 = + slow_path (dm, &key0, &s0, next0, &error0, thread_index); + if (PREDICT_FALSE (next0 == DSLITE_IN2OUT_NEXT_DROP)) + goto trace0; + } + else + { + next0 = DSLITE_IN2OUT_NEXT_SLOWPATH; + goto trace0; + } + } + else + { + s0 = + pool_elt_at_index (dm->per_thread_data[thread_index].sessions, + value0.value); + } + + old_addr0 = ip40->src_address.as_u32; + ip40->src_address = s0->out2in.addr; + new_addr0 = ip40->src_address.as_u32; + sum0 = ip40->checksum; + sum0 = + ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + src_address); + ip40->checksum = ip_csum_fold (sum0); + if (PREDICT_TRUE (proto0 == SNAT_PROTOCOL_TCP)) + { + old_port0 = tcp0->src_port; + tcp0->src_port = s0->out2in.port; + new_port0 = tcp0->src_port; + + sum0 = tcp0->checksum; + sum0 = + ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + dst_address); + sum0 = + ip_csum_update (sum0, old_port0, new_port0, ip4_header_t, + length); + //mss_clamping (&dslite_main, tcp0, &sum0); + tcp0->checksum = ip_csum_fold (sum0); + } + else + { + old_port0 = udp0->src_port; + udp0->src_port = s0->out2in.port; + udp0->checksum = 0; + } + + accounting0: + /* Accounting */ + s0->last_heard = now; + s0->total_pkts++; + s0->total_bytes += vlib_buffer_length_in_chain (vm, b0); + /* Per-B4 LRU list maintenance */ + clib_dlist_remove (dm->per_thread_data[thread_index].list_pool, + s0->per_b4_index); + clib_dlist_addtail (dm->per_thread_data[thread_index].list_pool, + s0->per_b4_list_head_index, s0->per_b4_index); + + 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; + t->session_index = ~0; + if (s0) + t->session_index = + s0 - dm->per_thread_data[thread_index].sessions; + } + + b0->error = 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; +} + +VLIB_NODE_FN (dslite_in2out_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return dslite_in2out_node_fn_inline (vm, node, frame, 0); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (dslite_in2out_node) = { + .name = "dslite-in2out", + .vector_size = sizeof (u32), + .format_trace = format_dslite_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (dslite_in2out_error_strings), + .error_strings = dslite_in2out_error_strings, + .n_next_nodes = DSLITE_IN2OUT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = { + [DSLITE_IN2OUT_NEXT_DROP] = "error-drop", + [DSLITE_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup", + [DSLITE_IN2OUT_NEXT_IP6_ICMP] = "ip6-icmp-input", + [DSLITE_IN2OUT_NEXT_SLOWPATH] = "dslite-in2out-slowpath", + }, +}; +/* *INDENT-ON* */ + +VLIB_NODE_FN (dslite_in2out_slowpath_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return dslite_in2out_node_fn_inline (vm, node, frame, 1); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (dslite_in2out_slowpath_node) = { + .name = "dslite-in2out-slowpath", + .vector_size = sizeof (u32), + .format_trace = format_dslite_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (dslite_in2out_error_strings), + .error_strings = dslite_in2out_error_strings, + .n_next_nodes = DSLITE_IN2OUT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = { + [DSLITE_IN2OUT_NEXT_DROP] = "error-drop", + [DSLITE_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup", + [DSLITE_IN2OUT_NEXT_IP6_ICMP] = "ip6-lookup", + [DSLITE_IN2OUT_NEXT_SLOWPATH] = "dslite-in2out-slowpath", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/dslite/dslite_out2in.c b/src/plugins/nat/dslite/dslite_out2in.c new file mode 100644 index 00000000000..ac64a589509 --- /dev/null +++ b/src/plugins/nat/dslite/dslite_out2in.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2017 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 <nat/dslite/dslite.h> +#include <nat/nat_inlines.h> + +typedef enum +{ + DSLITE_OUT2IN_NEXT_IP4_LOOKUP, + DSLITE_OUT2IN_NEXT_IP6_LOOKUP, + DSLITE_OUT2IN_NEXT_DROP, + DSLITE_OUT2IN_N_NEXT, +} dslite_out2in_next_t; + +static char *dslite_out2in_error_strings[] = { +#define _(sym,string) string, + foreach_dslite_error +#undef _ +}; + +static inline u32 +dslite_icmp_out2in (dslite_main_t * dm, ip4_header_t * ip4, + dslite_session_t ** sp, u32 next, u8 * error, + u32 thread_index) +{ + dslite_session_t *s = 0; + icmp46_header_t *icmp = ip4_next_header (ip4); + clib_bihash_kv_8_8_t kv, value; + snat_session_key_t key; + u32 n = next; + icmp_echo_header_t *echo; + u32 new_addr, old_addr; + u16 old_id, new_id; + ip_csum_t sum; + + echo = (icmp_echo_header_t *) (icmp + 1); + + if (icmp_type_is_error_message (icmp->type) + || (icmp->type != ICMP4_echo_reply)) + { + n = DSLITE_OUT2IN_NEXT_DROP; + *error = DSLITE_ERROR_BAD_ICMP_TYPE; + goto done; + } + + key.addr = ip4->dst_address; + key.port = echo->identifier; + key.protocol = SNAT_PROTOCOL_ICMP; + key.fib_index = 0; + kv.key = key.as_u64; + + if (clib_bihash_search_8_8 + (&dm->per_thread_data[thread_index].out2in, &kv, &value)) + { + next = DSLITE_OUT2IN_NEXT_DROP; + *error = DSLITE_ERROR_NO_TRANSLATION; + goto done; + } + else + { + s = + pool_elt_at_index (dm->per_thread_data[thread_index].sessions, + value.value); + } + + old_id = echo->identifier; + echo->identifier = new_id = s->in2out.port; + sum = icmp->checksum; + sum = ip_csum_update (sum, old_id, new_id, icmp_echo_header_t, identifier); + icmp->checksum = ip_csum_fold (sum); + + old_addr = ip4->dst_address.as_u32; + ip4->dst_address = s->in2out.addr; + new_addr = ip4->dst_address.as_u32; + + sum = ip4->checksum; + sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address); + ip4->checksum = ip_csum_fold (sum); + +done: + *sp = s; + return n; +} + +VLIB_NODE_FN (dslite_out2in_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + dslite_out2in_next_t next_index; + vlib_node_runtime_t *error_node; + u32 thread_index = vm->thread_index; + f64 now = vlib_time_now (vm); + dslite_main_t *dm = &dslite_main; + + error_node = vlib_node_get_runtime (vm, dm->dslite_out2in_node_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_OUT2IN_NEXT_IP6_LOOKUP; + u8 error0 = DSLITE_ERROR_OUT2IN; + ip4_header_t *ip40; + ip6_header_t *ip60; + u32 proto0; + udp_header_t *udp0; + tcp_header_t *tcp0; + clib_bihash_kv_8_8_t kv0, value0; + snat_session_key_t key0; + dslite_session_t *s0 = 0; + ip_csum_t sum0; + u32 new_addr0, old_addr0; + u16 new_port0, old_port0; + + /* 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_OUT2IN_NEXT_DROP; + goto trace0; + } + + if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = + dslite_icmp_out2in (dm, ip40, &s0, next0, &error0, + thread_index); + if (PREDICT_FALSE (next0 == DSLITE_OUT2IN_NEXT_DROP)) + goto trace0; + + goto encap0; + } + + udp0 = ip4_next_header (ip40); + tcp0 = (tcp_header_t *) udp0; + + key0.addr = ip40->dst_address; + key0.port = udp0->dst_port; + key0.protocol = proto0; + key0.fib_index = 0; + kv0.key = key0.as_u64; + + if (clib_bihash_search_8_8 + (&dm->per_thread_data[thread_index].out2in, &kv0, &value0)) + { + next0 = DSLITE_OUT2IN_NEXT_DROP; + error0 = DSLITE_ERROR_NO_TRANSLATION; + goto trace0; + } + else + { + s0 = + pool_elt_at_index (dm->per_thread_data[thread_index].sessions, + value0.value); + } + + old_addr0 = ip40->dst_address.as_u32; + ip40->dst_address = s0->in2out.addr; + new_addr0 = ip40->dst_address.as_u32; + + sum0 = ip40->checksum; + sum0 = + ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + dst_address); + ip40->checksum = ip_csum_fold (sum0); + + if (PREDICT_TRUE (proto0 == SNAT_PROTOCOL_TCP)) + { + old_port0 = tcp0->dst_port; + tcp0->dst_port = s0->in2out.port; + new_port0 = tcp0->dst_port; + + sum0 = tcp0->checksum; + sum0 = + ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + dst_address); + sum0 = + ip_csum_update (sum0, old_port0, new_port0, ip4_header_t, + length); + tcp0->checksum = ip_csum_fold (sum0); + } + else + { + old_port0 = udp0->dst_port; + udp0->dst_port = s0->in2out.port; + udp0->checksum = 0; + } + + encap0: + /* 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->src_address.as_u64[0] = dm->aftr_ip6_addr.as_u64[0]; + ip60->src_address.as_u64[1] = dm->aftr_ip6_addr.as_u64[1]; + ip60->dst_address.as_u64[0] = s0->in2out.softwire_id.as_u64[0]; + ip60->dst_address.as_u64[1] = s0->in2out.softwire_id.as_u64[1]; + + /* Accounting */ + s0->last_heard = now; + s0->total_pkts++; + s0->total_bytes += vlib_buffer_length_in_chain (vm, b0); + /* Per-B4 LRU list maintenance */ + clib_dlist_remove (dm->per_thread_data[thread_index].list_pool, + s0->per_b4_index); + clib_dlist_addtail (dm->per_thread_data[thread_index].list_pool, + s0->per_b4_list_head_index, s0->per_b4_index); + 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; + t->session_index = ~0; + if (s0) + t->session_index = + s0 - dm->per_thread_data[thread_index].sessions; + } + + b0->error = 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_out2in_node) = { + .name = "dslite-out2in", + .vector_size = sizeof (u32), + .format_trace = format_dslite_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (dslite_out2in_error_strings), + .error_strings = dslite_out2in_error_strings, + .n_next_nodes = DSLITE_OUT2IN_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = { + [DSLITE_OUT2IN_NEXT_DROP] = "error-drop", + [DSLITE_OUT2IN_NEXT_IP4_LOOKUP] = "ip4-lookup", + [DSLITE_OUT2IN_NEXT_IP6_LOOKUP] = "ip6-lookup", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |