diff --git a/dash-pipeline/vpp-plugin/CMakeLists.txt b/dash-pipeline/vpp-plugin/CMakeLists.txt new file mode 100644 index 000000000..ab20bbdc8 --- /dev/null +++ b/dash-pipeline/vpp-plugin/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.5) + +project(dash-plugin) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I /SAI/SAI/inc -I /SAI/SAI/experimental") + +find_package(VPP) + +add_subdirectory(dash) + +add_vpp_packaging( + NAME "dash-plugin" + VENDOR "fd.io" + DESCRIPTION "VPP Dash Plugin" +) diff --git a/dash-pipeline/vpp-plugin/Makefile b/dash-pipeline/vpp-plugin/Makefile new file mode 100644 index 000000000..378c55f06 --- /dev/null +++ b/dash-pipeline/vpp-plugin/Makefile @@ -0,0 +1,24 @@ +SHELL=/bin/bash +BUILD_DIR=build +CMAKE_ARGS= + +ifeq ($(V),1) +CMAKE_ARGS += --verbose +endif + +all: vpp + +.PHONY:configure install clean + +configure: + @cmake $(CMAKE_ARGS) -G Ninja -S . -B $(BUILD_DIR) + +vpp: configure + @cmake --build $(BUILD_DIR) $(CMAKE_ARGS) + +clean: + @cmake --build $(BUILD_DIR) $(CMAKE_ARGS) -- clean + +install: + @sudo cmake --build $(BUILD_DIR) $(CMAKE_ARGS) -- install + diff --git a/dash-pipeline/vpp-plugin/dash/CMakeLists.txt b/dash-pipeline/vpp-plugin/dash/CMakeLists.txt new file mode 100644 index 000000000..fc8c1e564 --- /dev/null +++ b/dash-pipeline/vpp-plugin/dash/CMakeLists.txt @@ -0,0 +1,36 @@ +# 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_directories(${CMAKE_SOURCE_DIR}) + +# for generated API headers: +include_directories(${CMAKE_BINARY_DIR}) + +add_vpp_plugin(dash + SOURCES + node.c + dash.c + flow.c + saiapi.c + + MULTIARCH_SOURCES + node.c + + API_FILES + dash.api + + API_TEST_SOURCES + dash_test.c + + COMPONENT vpp-plugin-dash +) diff --git a/dash-pipeline/vpp-plugin/dash/dash.api b/dash-pipeline/vpp-plugin/dash/dash.api new file mode 100644 index 000000000..a25a5f10b --- /dev/null +++ b/dash-pipeline/vpp-plugin/dash/dash.api @@ -0,0 +1,34 @@ +/* Hey Emacs use -*- mode: C -*- */ +/* + * Copyright (c) 2015 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. + */ + +/* Define a simple binary API to control the feature */ + +option version = "0.1.0"; +import "vnet/interface_types.api"; + +autoreply define dash_enable_disable { + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* Enable / disable the feature */ + bool enable_disable; + + /* Interface handle */ + vl_api_interface_index_t sw_if_index; +}; diff --git a/dash-pipeline/vpp-plugin/dash/dash.c b/dash-pipeline/vpp-plugin/dash/dash.c new file mode 100644 index 000000000..1e8ceadbd --- /dev/null +++ b/dash-pipeline/vpp-plugin/dash/dash.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2015 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. + */ +/** + * @file + * @brief Dash Plugin, plugin API / trace / CLI handling. + */ + +#include +#include +#include + +#include +#include + +#include +#include + +#define REPLY_MSG_ID_BASE sm->msg_id_base +#include + +/* *INDENT-OFF* */ +VLIB_PLUGIN_REGISTER () = { + .version = DASH_PLUGIN_BUILD_VER, + .description = "Dash of VPP Plugin", +}; +/* *INDENT-ON* */ + +VLIB_REGISTER_LOG_CLASS (dash_log) = { + .class_name = "dash", +}; + +dash_main_t dash_main; + +/** + * @brief Enable/disable the dash plugin. + * + * Action function shared between message handler and debug CLI. + */ + +int dash_enable_disable (dash_main_t * sm, u32 sw_if_index, + int enable_disable) +{ + vnet_sw_interface_t * sw; + int rv = 0; + + /* Utterly wrong? */ + if (pool_is_free_index (sm->vnet_main->interface_main.sw_interfaces, + sw_if_index)) + return VNET_API_ERROR_INVALID_SW_IF_INDEX; + + /* Not a physical port? */ + sw = vnet_get_sw_interface (sm->vnet_main, sw_if_index); + if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE) + return VNET_API_ERROR_INVALID_SW_IF_INDEX; + + vnet_feature_enable_disable ("dash-pipeline", "dash-pipeline-input", + sw_if_index, enable_disable, 0, 0); + + return rv; +} + +static clib_error_t * +dash_cmd_set_enable_disable_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + dash_main_t * sm = &dash_main; + u32 sw_if_index = ~0; + int enable_disable = 1; + int rv; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { + if (unformat (input, "disable")) + enable_disable = 0; + else if (unformat (input, "%U", unformat_vnet_sw_interface, + sm->vnet_main, &sw_if_index)) + ; + else + break; + } + + if (sw_if_index == ~0) + return clib_error_return (0, "Please specify an interface..."); + + rv = dash_enable_disable (sm, sw_if_index, enable_disable); + + switch(rv) { + case 0: + break; + + case VNET_API_ERROR_INVALID_SW_IF_INDEX: + return clib_error_return + (0, "Invalid interface, only works on physical ports"); + break; + + case VNET_API_ERROR_UNIMPLEMENTED: + return clib_error_return (0, "Device driver doesn't support redirection"); + break; + + default: + return clib_error_return (0, "dash_enable_disable returned %d", + rv); + } + return 0; +} + +/** + * @brief CLI command to enable/disable the dash plugin. + */ +VLIB_CLI_COMMAND (dash_set_command, static) = { + .path = "set dash", + .short_help = + "set dash [disable]", + .function = dash_cmd_set_enable_disable_fn, +}; + +/** + * @brief Plugin API message handler. + */ +static void vl_api_dash_enable_disable_t_handler +(vl_api_dash_enable_disable_t * mp) +{ + vl_api_dash_enable_disable_reply_t * rmp; + dash_main_t * sm = &dash_main; + int rv; + + rv = dash_enable_disable (sm, ntohl(mp->sw_if_index), + (int) (mp->enable_disable)); + + REPLY_MACRO(VL_API_DASH_ENABLE_DISABLE_REPLY); +} + +/* API definitions */ +#include + +/** + * @brief Initialize the dash plugin. + */ +static clib_error_t * dash_init (vlib_main_t * vm) +{ + dash_main_t * sm = &dash_main; + + sm->vnet_main = vnet_get_main (); + + /* Add our API messages to the global name_crc hash table */ + sm->msg_id_base = setup_message_id_table (); + + /* Reuse SECURE_DATA (0x876D) for dash metadata */ + ethernet_register_input_type (vm, ETHERNET_TYPE_SECURE_DATA, dash_node.index); + + dash_flow_table_init(dash_flow_table_get()); + + dash_sai_init(); + + return 0; +} + +VLIB_INIT_FUNCTION (dash_init); + +/** + * @brief Hook the dash plugin into the VPP graph hierarchy. + */ +VNET_FEATURE_ARC_INIT (dash_pipeline, static) = +{ + .arc_name = "dash-pipeline", + .start_nodes = VNET_FEATURES ("dash-pipeline-input"), + .last_in_arc = "error-drop", + .arc_index_ptr = &dash_main.feature_arc_index, +}; + +static uword +dash_timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) +{ + + int i; + f64 sleep_duration = 1.0; + + unix_sleep (5.0); /* FIXME: delay 5s */ + + while (1) + { + /* FIXME: Use time-wheel per-worker thread later */ + //for (i = 1; i < vlib_get_n_threads(); i++) { + vlib_node_set_interrupt_pending (vlib_get_main_by_index(1), + dash_flow_scan_node.index); + //} + + vlib_process_suspend (vm, sleep_duration); + } + return 0; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (dash_timer_node,static) = { + .function = dash_timer_process, + .name = "dash-timer-process", + .type = VLIB_NODE_TYPE_PROCESS, +}; +/* *INDENT-ON* */ + diff --git a/dash-pipeline/vpp-plugin/dash/dash.h b/dash-pipeline/vpp-plugin/dash/dash.h new file mode 100644 index 000000000..fab624452 --- /dev/null +++ b/dash-pipeline/vpp-plugin/dash/dash.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015 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_dash_h__ +#define __included_dash_h__ + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +typedef struct { + /* API message ID base */ + u16 msg_id_base; + + /* convenience */ + vnet_main_t * vnet_main; + + /* dash pipeline arc index */ + u8 feature_arc_index; +} dash_main_t; + +extern dash_main_t dash_main; + +extern vlib_node_registration_t dash_node; + +extern vlib_log_class_registration_t dash_log; + +#define dash_log_err(fmt, ...) \ + vlib_log_err (dash_log.class, fmt, ##__VA_ARGS__) + +#define dash_log_warn(fmt, ...) \ + vlib_log_warn (dash_log.class, fmt, ##__VA_ARGS__) + +#define dash_log_notice(fmt, ...) \ + vlib_log_notice (dash_log.class, fmt, ##__VA_ARGS__) + +#define dash_log_info(fmt, ...) \ + vlib_log_info (dash_log.class, fmt, ##__VA_ARGS__) + +#define dash_log_debug(fmt, ...) \ + vlib_log_debug (dash_log.class, fmt, ##__VA_ARGS__) + + +#define ASSERT_MSG(expr, message) \ + do { \ + if (!(expr)) { \ + dash_log_err("Assertion failed: (%s), %s:%d %s", \ + #expr, __FILE__, __LINE__, message);\ + abort(); \ + } \ + } while (0) + +void dash_sai_init (); +sai_status_t dash_sai_create_flow_entry (const dash_flow_entry_t *flow); +sai_status_t dash_sai_remove_flow_entry (const dash_flow_entry_t *flow); + +#define DASH_PLUGIN_BUILD_VER "1.0" + +#endif /* __included_dash_h__ */ diff --git a/dash-pipeline/vpp-plugin/dash/dash_test.c b/dash-pipeline/vpp-plugin/dash/dash_test.c new file mode 100644 index 000000000..fe9200bbb --- /dev/null +++ b/dash-pipeline/vpp-plugin/dash/dash_test.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015 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. + */ +/* + *------------------------------------------------------------------ + * dash_test.c - test harness plugin + *------------------------------------------------------------------ + */ + +#include +#include +#include +#include + +#define __plugin_msg_base dash_test_main.msg_id_base +#include + +uword unformat_sw_if_index (unformat_input_t * input, va_list * args); + +/* Declare message IDs */ +#include +#include + +typedef struct { + /* API message ID base */ + u16 msg_id_base; + vat_main_t *vat_main; +} dash_test_main_t; + +dash_test_main_t dash_test_main; + +static int api_dash_enable_disable (vat_main_t * vam) +{ + unformat_input_t * i = vam->input; + int enable_disable = 1; + u32 sw_if_index = ~0; + vl_api_dash_enable_disable_t * mp; + int ret; + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { + if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index)) + ; + else if (unformat (i, "sw_if_index %d", &sw_if_index)) + ; + else if (unformat (i, "disable")) + enable_disable = 0; + else + break; + } + + if (sw_if_index == ~0) { + errmsg ("missing interface name / explicit sw_if_index number \n"); + return -99; + } + + /* Construct the API message */ + M(DASH_ENABLE_DISABLE, mp); + mp->sw_if_index = ntohl (sw_if_index); + mp->enable_disable = enable_disable; + + /* send it... */ + S(mp); + + /* Wait for a reply... */ + W (ret); + return ret; +} + +/* + * List of messages that the api test plugin sends, + * and that the data plane plugin processes + */ +#include diff --git a/dash-pipeline/vpp-plugin/dash/flow.c b/dash-pipeline/vpp-plugin/dash/flow.c new file mode 100644 index 000000000..c02310966 --- /dev/null +++ b/dash-pipeline/vpp-plugin/dash/flow.c @@ -0,0 +1,401 @@ + +#include + +#include +#include + +#define DASH_FLOW_NUM (1 << 20) /* 1M */ +#define DASH_FLOW_NUM_BUCKETS (DASH_FLOW_NUM / BIHASH_KVP_PER_PAGE) +#define DASH_FLOW_MEMORY_SIZE (DASH_FLOW_NUM * 32) + +static dash_flow_table_t dash_flow_table; + +dash_flow_table_t* +dash_flow_table_get (void) +{ + return &dash_flow_table; +} + +dash_flow_entry_t* +dash_flow_alloc() +{ + dash_flow_entry_t *flow; + dash_flow_table_t *flow_table = dash_flow_table_get(); + + pool_get (flow_table->flow_pool, flow); + clib_memset (flow, 0, sizeof (*flow)); + flow->index = flow - flow_table->flow_pool; + flow->timeout = DASH_FLOW_TIMEOUT; + flow->access_time = (u64)unix_time_now(); + return flow; +} + +void +dash_flow_free(dash_flow_entry_t *flow) +{ + dash_flow_table_t *flow_table = dash_flow_table_get(); + + if (flow != NULL) + pool_put(flow_table->flow_pool, flow); +} + +dash_flow_entry_t* +dash_flow_get_by_index (u32 index) +{ + dash_flow_table_t *flow_table = dash_flow_table_get(); + dash_flow_entry_t *flow = pool_elt_at_index(flow_table->flow_pool, index); + + return flow; +} + +static int +dash_flow_create (dash_flow_table_t *flow_table, const dash_header_t *dh) +{ + int r; + sai_status_t status; + + ASSERT(flow_table && dh); + + u16 length = ntohs(dh->packet_meta.length); + ASSERT_MSG(length >= offsetof(dash_header_t, flow_overlay_data), "dash header not enough"); + + dash_flow_entry_t* flow = dash_flow_alloc(); + + clib_memcpy_fast(&flow->key, &dh->flow_key, sizeof(dh->flow_key)); + + clib_memcpy_fast(&flow->flow_data, &dh->flow_data, sizeof(dh->flow_data)); + + /* FIXME + * Assume overlay_data, encap_data, tunnel_data in order if exists + * Need to add their offset in generic. + */ + if (flow->flow_data.routing_actions != 0) { + ASSERT_MSG(length >= offsetof(dash_header_t, flow_encap_data), + "dash header not enough"); + clib_memcpy_fast(&flow->flow_overlay_data, &dh->flow_overlay_data, sizeof(dh->flow_overlay_data)); + } + + if (flow->flow_data.routing_actions & htonl(SAI_DASH_ROUTING_ACTIONS_STATIC_ENCAP)) { + ASSERT_MSG(length >= offsetof(dash_header_t, flow_tunnel_data), + "dash header not enough"); + clib_memcpy_fast(&flow->flow_encap_data, &dh->flow_encap_data, sizeof(dh->flow_encap_data)); + } + + if (flow->flow_data.tunnel_id != 0) { + ASSERT_MSG((u8*)(&dh->flow_tunnel_data + 1) <= (u8*)dh + length, + "dash header not enough"); + clib_memcpy_fast(&flow->flow_tunnel_data, &dh->flow_tunnel_data, sizeof(dh->flow_tunnel_data)); + } + + r = dash_flow_table_add_entry (flow_table, flow); + if (r != 0) goto table_add_entry_fail; + + status = dash_sai_create_flow_entry(flow); + if (status != SAI_STATUS_SUCCESS) goto sai_create_flow_fail; + + flow_table->flow_stats.create_ok++; + flow->timer_handle = TW (tw_timer_start) (&flow_table->flow_tw, flow->index, 0, flow->timeout); + return 0; + +sai_create_flow_fail: + flow_table->flow_stats.create_fail++; + dash_flow_table_delete_entry (flow_table, flow); + dash_flow_free(flow); + return -1; + +table_add_entry_fail: + dash_flow_free(flow); + return r; +} + +static int +dash_flow_update (dash_flow_table_t *flow_table, const dash_header_t *dh) +{ + return -1; /* TODO later */ +} + +static int +dash_flow_remove (dash_flow_table_t *flow_table, const dash_header_t *dh) +{ + int r = -1; + sai_status_t status; + dash_flow_hash_key_t flow_hash_key; + dash_flow_entry_t* flow; + + ASSERT(flow_table && dh); + + u16 length = ntohs(dh->packet_meta.length); + ASSERT(length >= offsetof(dash_header_t, flow_key)); + + bzero(&flow_hash_key, sizeof(flow_hash_key)); + clib_memcpy_fast(&flow_hash_key, &dh->flow_key, sizeof(dh->flow_key)); + + flow = dash_flow_table_lookup_entry(flow_table, &flow_hash_key.key); + if (!flow) goto flow_not_found; + + status = dash_sai_remove_flow_entry(flow); + if (status != SAI_STATUS_SUCCESS) goto sai_remove_flow_fail; + + TW (tw_timer_stop) (&flow_table->flow_tw, flow->timer_handle); + + r = dash_flow_table_delete_entry (flow_table, flow); + ASSERT(r == 0); + dash_flow_free(flow); + + flow_table->flow_stats.remove_ok++; + return 0; + +sai_remove_flow_fail: + flow_table->flow_stats.remove_fail++; + +flow_not_found: + return -1; +} + +typedef int (*dash_flow_cmd_handler) (dash_flow_table_t *flow_table, const dash_header_t *dh); + +static dash_flow_cmd_handler flow_cmd_funs[] = { + [1] = dash_flow_create, + [2] = dash_flow_update, + [3] = dash_flow_remove, +}; + + +int +dash_flow_process (dash_flow_table_t *flow_table, const dash_header_t *dh) +{ + ASSERT(dh->packet_meta.packet_type == 0); + ASSERT(dh->packet_meta.packet_subtype > 0); + ASSERT(dh->packet_meta.packet_subtype < 4); + + return flow_cmd_funs[dh->packet_meta.packet_subtype](flow_table, dh); +} + +static void +dash_flow_expired_timer_callback (u32 * expired_timers) +{ + int i; + u32 index; + sai_status_t status; + dash_flow_table_t *flow_table = dash_flow_table_get(); + + for (i = 0; i < vec_len (expired_timers); i++) + { + index = expired_timers[i] & 0x7FFFFFFF; + dash_flow_entry_t *flow = dash_flow_get_by_index(index); + status = dash_sai_remove_flow_entry(flow); + if (status != SAI_STATUS_SUCCESS) { + dash_log_err("dash_sai_remove_flow_entry fail: %d", status); + continue; + } + + if (dash_flow_table_delete_entry (flow_table, flow) == 0) { + dash_flow_free(flow); + } else { + ASSERT(0); + } + } +} + +void +dash_flow_table_init (dash_flow_table_t *flow_table) +{ + BV(clib_bihash_init) (&flow_table->hash_table, "flow hash table", + DASH_FLOW_NUM_BUCKETS, DASH_FLOW_MEMORY_SIZE); + + pool_init_fixed (flow_table->flow_pool, DASH_FLOW_NUM); + + bzero(&flow_table->flow_stats, sizeof(flow_table->flow_stats)); + + TW (tw_timer_wheel_init) (&flow_table->flow_tw, + dash_flow_expired_timer_callback, + 1.0 /* timer interval */, 1024); +} + +int +dash_flow_table_add_entry (dash_flow_table_t *flow_table, dash_flow_entry_t *flow) +{ + BVT (clib_bihash_kv) kv; + + clib_memcpy_fast (kv.key, &flow->key, sizeof(kv.key)); + kv.value = (u64)(uintptr_t)&flow->flow_data; + return BV (clib_bihash_add_del) (&flow_table->hash_table, &kv, 1 /* is_add */ ); +} + +int +dash_flow_table_delete_entry (dash_flow_table_t *flow_table, dash_flow_entry_t *flow) +{ + BVT (clib_bihash_kv) kv; + + clib_memcpy_fast (kv.key, &flow->key, sizeof(kv.key)); + return BV (clib_bihash_add_del) (&flow_table->hash_table, &kv, 0 /* is_del */ ); +} + +dash_flow_entry_t* +dash_flow_table_lookup_entry (dash_flow_table_t *flow_table, flow_key_t *flow_key) +{ + BVT (clib_bihash_kv) kv; + flow_data_t *flow_data; + + clib_memcpy_fast (kv.key, flow_key, sizeof(kv.key)); + if (BV (clib_bihash_search) (&flow_table->hash_table, &kv, &kv)) + return NULL; + + flow_data = (flow_data_t *)(uintptr_t)kv.value; + return (dash_flow_entry_t*)((u8*)flow_data - offsetof(dash_flow_entry_t, flow_data)); +} + +static uword +dash_flow_scan (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) +{ + dash_flow_table_t *flow_table = dash_flow_table_get(); + TW (tw_timer_expire_timers) (&flow_table->flow_tw, vlib_time_now(vm)); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (dash_flow_scan_node) = { + .function = dash_flow_scan, + .name = "dash-flow-scan", + .type = VLIB_NODE_TYPE_INPUT, + .state = VLIB_NODE_STATE_INTERRUPT, +}; +/* *INDENT-ON* */ + +static u8 * +dash_flow_format (u8 * s, va_list * args) +{ + dash_flow_entry_t *flow = va_arg(*args, dash_flow_entry_t*); + + s = format (s, "eni %U, vnet_id %d, proto %d, ", + format_mac_address, flow->key.eni_mac, + clib_net_to_host_u16 (flow->key.vnet_id), + flow->key.ip_proto); + + if (flow->key.is_ip_v6) { + s = format (s, "%U %d -> %U %d\n", + format_ip6_address, &flow->key.src_ip.ip6, + clib_net_to_host_u16 (flow->key.src_port), + format_ip6_address, &flow->key.dst_ip.ip6, + clib_net_to_host_u16 (flow->key.dst_port)); + } else { + s = format (s, "%U %d -> %U %d\n", + format_ip4_address, &flow->key.src_ip.ip4, + clib_net_to_host_u16 (flow->key.src_port), + format_ip4_address, &flow->key.dst_ip.ip4, + clib_net_to_host_u16 (flow->key.dst_port)); + } + + s = format (s, " common data - version %u, direction %u, actions %u", + clib_net_to_host_u32 (flow->flow_data.version), + clib_net_to_host_u16 (flow->flow_data.direction), + clib_net_to_host_u32 (flow->flow_data.routing_actions)); + s = format (s, ", timeout %lu\n", + flow->access_time + flow->timeout - (u64)unix_time_now()); + + return s; +} + +typedef struct dash_flow_show_walk_ctx_t_ +{ + u8 verbose; + vlib_main_t *vm; +} dash_flow_show_walk_ctx_t; + +static int +dash_flow_show_walk_cb (BVT (clib_bihash_kv) * kvp, void *arg) +{ + dash_flow_show_walk_ctx_t *ctx = arg; + flow_data_t *flow_data = (flow_data_t *)(uintptr_t)kvp->value; + dash_flow_entry_t *flow = (dash_flow_entry_t*)((u8*)flow_data - + offsetof(dash_flow_entry_t, flow_data)); + + vlib_cli_output (ctx->vm, "%6u: %U", flow->index, dash_flow_format, flow); +} + +static clib_error_t * +dash_cmd_show_flow_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + clib_error_t *error = 0; + dash_flow_show_walk_ctx_t ctx = { + .vm = vm, + }; + dash_flow_table_t *flow_table = dash_flow_table_get(); + + BV (clib_bihash_foreach_key_value_pair) + (&flow_table->hash_table, dash_flow_show_walk_cb, &ctx); + + return error; +} + +VLIB_CLI_COMMAND (dash_show_flow_command, static) = { + .path = "show dash flow", + .short_help = "show dash flow [src-addr IP]", + .function = dash_cmd_show_flow_fn, +}; + +static clib_error_t * +dash_cmd_clear_flow_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + clib_error_t *error = 0; + dash_flow_table_t *flow_table = dash_flow_table_get(); + u32 index; + + if (!unformat (input, "%u", &index)) + { + error = clib_error_return (0, "expected flow index"); + goto done; + } + + + dash_flow_entry_t *flow = dash_flow_get_by_index(index); + sai_status_t status = dash_sai_remove_flow_entry(flow); + if (status != SAI_STATUS_SUCCESS) { + error = clib_error_return (0, "dash_sai_remove_flow_entry fail: %d", status); + } + + TW (tw_timer_stop) (&flow_table->flow_tw, flow->timer_handle); + + if (dash_flow_table_delete_entry (flow_table, flow) == 0) { + dash_flow_free(flow); + } else { + ASSERT(0); + } + +done: + return error; +} + +VLIB_CLI_COMMAND (dash_clear_flow_command, static) = { + .path = "clear dash flow", + .short_help = "clear dash flow ", + .function = dash_cmd_clear_flow_fn, +}; + + +static clib_error_t * +dash_cmd_show_flow_stats_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + clib_error_t *error = 0; + dash_flow_table_t *flow_table = dash_flow_table_get(); + + vlib_cli_output (vm, "%10s: %u", "create_ok", + flow_table->flow_stats.create_ok); + vlib_cli_output (vm, "%10s: %u", "create_fail", + flow_table->flow_stats.create_fail); + vlib_cli_output (vm, "%10s: %u", "remove_ok", + flow_table->flow_stats.remove_ok); + vlib_cli_output (vm, "%10s: %u", "remove_fail", + flow_table->flow_stats.remove_fail); + + return error; +} + +VLIB_CLI_COMMAND (dash_show_flow_stats_command, static) = { + .path = "show dash flow stats", + .short_help = "show dash flow [src-addr IP]", + .function = dash_cmd_show_flow_stats_fn, +}; + diff --git a/dash-pipeline/vpp-plugin/dash/flow.h b/dash-pipeline/vpp-plugin/dash/flow.h new file mode 100644 index 000000000..0a1a815d3 --- /dev/null +++ b/dash-pipeline/vpp-plugin/dash/flow.h @@ -0,0 +1,195 @@ +#ifndef __included_flow_h__ +#define __included_flow_h__ + +#include + +#include +#include +#include + +#include + +/* Default timeout in seconds */ +#define DASH_FLOW_TIMEOUT 30 + +typedef enum _dash_packet_source_t { + EXTERNAL = 0, // Packets from external sources. + PIPELINE = 1, // Packets from P4 pipeline. + DPAPP = 2, // Packets from data plane app. + PEER = 3 // Packets from the paired DPU. +} dash_packet_source_t; + +typedef enum _dash_packet_type_t { + REGULAR = 0, // Regular packets from external sources. + FLOW_SYNC_REQ = 1, // Flow sync request packet. + FLOW_SYNC_ACK = 2, // Flow sync ack packet. + DP_PROBE_REQ = 3, // Data plane probe packet. + DP_PROBE_ACK = 4 // Data plane probe ack packet. +} dash_packet_type_t; + +typedef enum _dash_packet_subtype_t { + NONE = 0, // no op + FLOW_CREATE = 1, // New flow creation. + FLOW_UPDATE = 2, // Flow resimulation or any other reason causing existing flow to be updated. + FLOW_DELETE = 3 // Flow deletion. +} dash_packet_subtype_t; + +typedef struct dash_packet_meta { + u8 packet_source; +#if defined(__LITTLE_ENDIAN_BITFIELD) + u8 packet_subtype :4; + u8 packet_type :4; +#elif defined (__BIG_ENDIAN_BITFIELD) + u8 packet_type :4; + u8 packet_subtype :4; +#else +#error "Please fix " +#endif + u16 length; +} __clib_packed dash_packet_meta_t; + +/* + * If sizeof flow_key_t > 48, update the use of bihash_xx + */ +typedef struct flow_key { + u8 eni_mac[6]; + u16 vnet_id; + ip46_address_t src_ip; + ip46_address_t dst_ip; + u16 src_port; + u16 dst_port; + u8 ip_proto; +#if defined(__LITTLE_ENDIAN_BITFIELD) + u8 is_ip_v6 :1; + u8 reserved :7; +#elif defined (__BIG_ENDIAN_BITFIELD) + u8 reserved :7; + u8 is_ip_v6 :1; +#else +#error "Please fix " +#endif +} __clib_packed flow_key_t; + +typedef union { + flow_key_t key; + u64 bihash_key[6]; /* bihash_48_8 */ +} dash_flow_hash_key_t; + +typedef struct flow_data { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u8 is_unidirectional :1; + u8 reserved :7; +#elif defined (__BIG_ENDIAN_BITFIELD) + u8 reserved :7; + u8 is_unidirectional :1; +#else +#error "Please fix " +#endif + u32 version; + u16 direction; + u16 tunnel_id; + u32 routing_actions; + u32 meter_class; +} __clib_packed flow_data_t; + +typedef struct encap_data { + u8 vni_high; + u16 vni_low; + u8 dest_vnet_vni_high; + u16 dest_vnet_vni_low; + ip4_address_t underlay_sip; + ip4_address_t underlay_dip; + u8 underlay_smac[6]; + u8 underlay_dmac[6]; + u16 dash_encapsulation; +} __clib_packed encap_data_t; + +typedef struct overlay_rewrite_data { + u8 dmac[6]; + ip46_address_t sip; + ip46_address_t dip; + ip6_address_t sip_mask; + ip6_address_t dip_mask; +#if defined(__LITTLE_ENDIAN_BITFIELD) + u8 is_ipv6 :1; + u8 reserved :7; +#elif defined (__BIG_ENDIAN_BITFIELD) + u8 reserved :7; + u8 is_ipv6 :1; +#else +#error "Please fix " +#endif +} __clib_packed overlay_rewrite_data_t; + + +typedef struct dash_header { + dash_packet_meta_t packet_meta; + union { + struct { + flow_key_t flow_key; + flow_data_t flow_data; // flow common data + overlay_rewrite_data_t flow_overlay_data; + encap_data_t flow_encap_data; + encap_data_t flow_tunnel_data; + }; + u8 data[0]; + }; +} +__clib_packed dash_header_t; + + +typedef struct dash_flow_entry { + union { + flow_key_t key; + u64 bihash_key[6]; /* bihash_48_8 */ + }; + + struct { + flow_data_t flow_data; + overlay_rewrite_data_t flow_overlay_data; + encap_data_t flow_encap_data; + encap_data_t flow_tunnel_data; + }; + + u32 index; + + /* timers */ + u32 timer_handle; /* index in the timer pool */ + u32 timeout; /* in seconds */ + u64 access_time; /* in seconds */ +} dash_flow_entry_t; + +typedef struct dash_flow_stats { + u32 create_ok; + u32 create_fail; + + u32 remove_ok; + u32 remove_fail; +} dash_flow_stats_t; + +typedef struct dash_flow_table { + /* hashtable */ + BVT(clib_bihash) hash_table; + + dash_flow_entry_t *flow_pool; + dash_flow_stats_t flow_stats; + + TWT (tw_timer_wheel) flow_tw; +} dash_flow_table_t; + +dash_flow_table_t* dash_flow_table_get (void); +dash_flow_entry_t* dash_flow_alloc(); +void dash_flow_free(dash_flow_entry_t *flow); +dash_flow_entry_t* dash_flow_get_by_index (u32 index); + +void dash_flow_table_init (dash_flow_table_t *flow_table); +int dash_flow_table_add_entry (dash_flow_table_t *flow_table, dash_flow_entry_t *flow); +int dash_flow_table_delete_entry (dash_flow_table_t *flow_table, dash_flow_entry_t *flow); +dash_flow_entry_t* dash_flow_table_lookup_entry (dash_flow_table_t *flow_table, flow_key_t *flow_key); + + +int dash_flow_process (dash_flow_table_t *flow_table, const dash_header_t *dh); + +extern vlib_node_registration_t dash_flow_scan_node; + +#endif /* __included_flow_h__ */ diff --git a/dash-pipeline/vpp-plugin/dash/node.c b/dash-pipeline/vpp-plugin/dash/node.c new file mode 100644 index 000000000..b8698a975 --- /dev/null +++ b/dash-pipeline/vpp-plugin/dash/node.c @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2015 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 + +#include +#include +#include +#include +#include +#include + +typedef struct +{ + u32 next_index; + u32 sw_if_index; + u8 new_src_mac[6]; + u8 new_dst_mac[6]; +} dash_trace_t; + + +/* packet trace format function */ +static u8 * +format_dash_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 *); + dash_trace_t *t = va_arg (*args, dash_trace_t *); + + s = format (s, "DASH: sw_if_index %d, next index %d\n", + t->sw_if_index, t->next_index); + s = format (s, " new src %U -> new dst %U", + format_mac_address, t->new_src_mac, + format_mac_address, t->new_dst_mac); + + return s; +} + +extern vlib_node_registration_t dash_node; + +#define foreach_dash_error \ +_(SWAPPED, "Mac swap packets processed") + +typedef enum +{ +#define _(sym,str) DASH_ERROR_##sym, + foreach_dash_error +#undef _ + DASH_N_ERROR, +} dash_error_t; + +static char *dash_error_strings[] = { +#define _(sym,string) string, + foreach_dash_error +#undef _ +}; + +typedef enum +{ + DASH_NEXT_INTERFACE_OUTPUT, + DASH_N_NEXT, +} dash_next_t; + +#define foreach_mac_address_offset \ +_(0) \ +_(1) \ +_(2) \ +_(3) \ +_(4) \ +_(5) + +VLIB_NODE_FN (dash_node) (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + dash_next_t next_index; + u32 pkts_swapped = 0; + + 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 >= 4 && n_left_to_next >= 2) + { + u32 next0 = DASH_NEXT_INTERFACE_OUTPUT; + u32 next1 = DASH_NEXT_INTERFACE_OUTPUT; + u32 sw_if_index0, sw_if_index1; + u8 tmp0[6], tmp1[6]; + ethernet_header_t *en0, *en1; + dash_header_t *dh0, *dh1; + u32 bi0, bi1; + vlib_buffer_t *b0, *b1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t *p2, *p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + clib_prefetch_store (p2->data); + clib_prefetch_store (p3->data); + } + + /* speculatively enqueue b0 and b1 to the current next frame */ + to_next[0] = bi0 = from[0]; + to_next[1] = bi1 = from[1]; + from += 2; + to_next += 2; + n_left_from -= 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + dh0 = vlib_buffer_get_current (b0); + dh1 = vlib_buffer_get_current (b1); + dash_flow_table_t *flow_table = dash_flow_table_get(); + if (dash_flow_process(flow_table, dh0) != 0) { + /* FIXME */ + } + if (dash_flow_process(flow_table, dh1) != 0) { + /* FIXME */ + } + + /* + * Build new packet + */ + + /* Update ethernet header via swap src and dst mac */ + en0 = (ethernet_header_t*)dh0 - 1; +#define _(a) tmp0[a] = en0->src_address[a]; + foreach_mac_address_offset; +#undef _ +#define _(a) en0->src_address[a] = en0->dst_address[a]; + foreach_mac_address_offset; +#undef _ +#define _(a) en0->dst_address[a] = tmp0[a]; + foreach_mac_address_offset; +#undef _ + + en1 = (ethernet_header_t*)dh1 - 1; +#define _(a) tmp1[a] = en1->src_address[a]; + foreach_mac_address_offset; +#undef _ +#define _(a) en1->src_address[a] = en1->dst_address[a]; + foreach_mac_address_offset; +#undef _ +#define _(a) en1->dst_address[a] = tmp1[a]; + foreach_mac_address_offset; +#undef _ + + /* Update dash header */ + + dh0->packet_meta.packet_source = DPAPP; + if (dh0->packet_meta.packet_subtype != FLOW_DELETE) { + /* Only keep packet_meta and flow_key in dash_header_t */ + u16 length0 = ntohs(dh0->packet_meta.length); + dh0->packet_meta.length = htons(offsetof(dash_header_t, flow_data)); + /* Move customer packet after dash header */ + clib_memmove((u8*)&dh0->flow_data, (u8*)dh0 + length0, vlib_buffer_get_tail(b0) - (u8*)dh0 - length0); + b0->current_length -= length0 - offsetof(dash_header_t, flow_data); + } + + dh1->packet_meta.packet_source = DPAPP; + if (dh1->packet_meta.packet_subtype != FLOW_DELETE) { + /* Only keep packet_meta and flow_key in dash_header_t */ + u16 length1 = ntohs(dh1->packet_meta.length); + dh1->packet_meta.length = htons(offsetof(dash_header_t, flow_data)); + /* Move customer packet after dash header */ + clib_memmove((u8*)&dh1->flow_data, (u8*)dh1 + length1, vlib_buffer_get_tail(b1) - (u8*)dh1 - length1); + b1->current_length -= length1 - offsetof(dash_header_t, flow_data); + } + + vlib_buffer_reset (b0); + vlib_buffer_reset (b1); + + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX]; + + /* Send pkt back out the RX interface */ + vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index0; + vnet_buffer (b1)->sw_if_index[VLIB_TX] = sw_if_index1; + + pkts_swapped += 2; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + if (b0->flags & VLIB_BUFFER_IS_TRACED) + { + dash_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + clib_memcpy_fast (t->new_src_mac, en0->src_address, + sizeof (t->new_src_mac)); + clib_memcpy_fast (t->new_dst_mac, en0->dst_address, + sizeof (t->new_dst_mac)); + + } + if (b1->flags & VLIB_BUFFER_IS_TRACED) + { + dash_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->sw_if_index = sw_if_index1; + t->next_index = next1; + clib_memcpy_fast (t->new_src_mac, en1->src_address, + sizeof (t->new_src_mac)); + clib_memcpy_fast (t->new_dst_mac, en1->dst_address, + sizeof (t->new_dst_mac)); + } + } + + /* verify speculative enqueues, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0 = DASH_NEXT_INTERFACE_OUTPUT; + u32 sw_if_index0; + u8 tmp0[6]; + ethernet_header_t *en0; + dash_header_t *dh0; + + /* 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); + dh0 = vlib_buffer_get_current (b0); + dash_flow_table_t *flow_table = dash_flow_table_get(); + if (dash_flow_process(flow_table, dh0) != 0) { + /* FIXME */ + } + + /* + * Build new packet + */ + + /* Update ethernet header via swap src and dst mac */ + en0 = (ethernet_header_t*)dh0 - 1; +#define _(a) tmp0[a] = en0->src_address[a]; + foreach_mac_address_offset; +#undef _ +#define _(a) en0->src_address[a] = en0->dst_address[a]; + foreach_mac_address_offset; +#undef _ +#define _(a) en0->dst_address[a] = tmp0[a]; + foreach_mac_address_offset; +#undef _ + + /* Update dash header */ + dh0->packet_meta.packet_source = DPAPP; + if (dh0->packet_meta.packet_subtype != FLOW_DELETE) { + /* Only keep packet_meta and flow_key in dash_header_t */ + u16 length0 = ntohs(dh0->packet_meta.length); + dh0->packet_meta.length = htons(offsetof(dash_header_t, flow_data)); + /* Move customer packet after dash header */ + clib_memmove((u8*)&dh0->flow_data, (u8*)dh0 + length0, vlib_buffer_get_tail(b0) - (u8*)dh0 - length0); + b0->current_length -= length0 - offsetof(dash_header_t, flow_data); + } + + vlib_buffer_reset (b0); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + + /* Send pkt back out the RX interface */ + vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index0; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + dash_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + clib_memcpy_fast (t->new_src_mac, en0->src_address, + sizeof (t->new_src_mac)); + clib_memcpy_fast (t->new_dst_mac, en0->dst_address, + sizeof (t->new_dst_mac)); + } + + pkts_swapped += 1; + + /* 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); + } + + vlib_node_increment_counter (vm, dash_node.index, + DASH_ERROR_SWAPPED, pkts_swapped); + return frame->n_vectors; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (dash_node) = +{ + .name = "dash-pipeline-input", + .vector_size = sizeof (u32), + .format_trace = format_dash_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(dash_error_strings), + .error_strings = dash_error_strings, + + .n_next_nodes = DASH_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [DASH_NEXT_INTERFACE_OUTPUT] = "interface-output", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/dash-pipeline/vpp-plugin/dash/saiapi.c b/dash-pipeline/vpp-plugin/dash/saiapi.c new file mode 100644 index 000000000..f7b3f8d53 --- /dev/null +++ b/dash-pipeline/vpp-plugin/dash/saiapi.c @@ -0,0 +1,242 @@ + +#include + +#include +#include + +#include +#include + +static sai_object_id_t dash_switch_id = SAI_NULL_OBJECT_ID; +static sai_dash_flow_api_t *dash_flow_api = NULL; + +void +dash_sai_init () +{ + sai_status_t status; + + status = sai_api_initialize(0, NULL); + ASSERT_MSG(status == SAI_STATUS_SUCCESS, "Failed to initialize SAI api"); + + sai_switch_api_t *switch_api; + status = sai_api_query((sai_api_t)SAI_API_SWITCH, (void**)&switch_api); + ASSERT_MSG(status == SAI_STATUS_SUCCESS, "Failed to query SAI_API_SWITCH"); + + status = switch_api->create_switch(&dash_switch_id, 0, NULL); + ASSERT_MSG(status == SAI_STATUS_SUCCESS, "Failed to create switch"); + + status = sai_api_query((sai_api_t)SAI_API_DASH_FLOW, (void**)&dash_flow_api); + ASSERT_MSG(status == SAI_STATUS_SUCCESS, "Failed to query SAI_API_DASH_FLOW"); + + dash_log_info("Succeeded to init dash sai api"); +} + +sai_status_t +dash_sai_create_flow_entry (const dash_flow_entry_t *flow) +{ + sai_flow_entry_t flow_entry; + u32 count = 0; + sai_attribute_t attrs[SAI_FLOW_ENTRY_ATTR_END]; + const flow_key_t *flow_key = &flow->key; + const flow_data_t *flow_data = &flow->flow_data; + const overlay_rewrite_data_t *flow_overlay_data = &flow->flow_overlay_data; + const encap_data_t *flow_encap_data = &flow->flow_encap_data; + const encap_data_t *flow_tunnel_data = &flow->flow_tunnel_data; + + /* + * Fill sai_flow_entry_t, sai_attribute_t, whose values need host order + * ip4/6 address in network order + */ + flow_entry.switch_id = dash_switch_id; + clib_memcpy_fast(flow_entry.eni_mac, flow_key->eni_mac, sizeof(flow_entry.eni_mac)); + flow_entry.vnet_id = ntohs(flow_key->vnet_id); + if (flow_key->is_ip_v6) { + flow_entry.src_ip.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + clib_memcpy_fast(flow_entry.src_ip.addr.ip6, &flow_key->src_ip.ip6, sizeof(sai_ip6_t)); + flow_entry.dst_ip.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + clib_memcpy_fast(flow_entry.dst_ip.addr.ip6, &flow_key->dst_ip.ip6, sizeof(sai_ip6_t)); + } else { + flow_entry.src_ip.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + flow_entry.src_ip.addr.ip4 = flow_key->src_ip.ip4.as_u32; + flow_entry.dst_ip.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + flow_entry.dst_ip.addr.ip4 = flow_key->dst_ip.ip4.as_u32; + } + flow_entry.src_port = ntohs(flow_key->src_port); + flow_entry.dst_port = ntohs(flow_key->dst_port); + flow_entry.ip_proto = flow_key->ip_proto; + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_IS_UNIDIRECTIONAL; + attrs[count++].value.booldata = flow_data->is_unidirectional; + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_STATE; + attrs[count++].value.u8 = SAI_DASH_FLOW_STATE_FLOW_CREATED; + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_VERSION; + attrs[count++].value.u32 = ntohl(flow_data->version); + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_DASH_DIRECTION; + attrs[count++].value.u16 = ntohs(flow_data->direction); + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_TUNNEL_ID; + attrs[count++].value.u16 = ntohs(flow_data->tunnel_id); + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_ROUTING_ACTIONS; + attrs[count++].value.u32 = ntohl(flow_data->routing_actions); + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_METER_CLASS; + attrs[count++].value.u32 = ntohl(flow_data->meter_class); + + /* Attrs for overlay rewrite data */ + if (flow_data->routing_actions != 0) { + attrs[count].id = SAI_FLOW_ENTRY_ATTR_OVERLAY_DATA_IS_IPV6; + attrs[count++].value.booldata = flow_overlay_data->is_ipv6; + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_OVERLAY_DATA_DST_MAC; + clib_memcpy_fast(attrs[count].value.mac, flow_overlay_data->dmac, sizeof(ethernet_header_t)); + count++; + + if (flow_overlay_data->is_ipv6) { + attrs[count].id = SAI_FLOW_ENTRY_ATTR_OVERLAY_DATA_SIP; + attrs[count].value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + clib_memcpy_fast(attrs[count].value.ipaddr.addr.ip6, + &flow_overlay_data->sip.ip6, sizeof(sai_ip6_t)); + count++; + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_OVERLAY_DATA_DIP; + attrs[count].value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + clib_memcpy_fast(attrs[count].value.ipaddr.addr.ip6, + &flow_overlay_data->dip.ip6, sizeof(sai_ip6_t)); + count++; + } else { + attrs[count].id = SAI_FLOW_ENTRY_ATTR_OVERLAY_DATA_SIP; + attrs[count].value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + attrs[count++].value.ipaddr.addr.ip4 = flow_overlay_data->sip.ip4.as_u32; + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_OVERLAY_DATA_DIP; + attrs[count].value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + attrs[count++].value.ipaddr.addr.ip4 = flow_overlay_data->dip.ip4.as_u32; + } + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_OVERLAY_DATA_SIP_MASK; + attrs[count].value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + clib_memcpy_fast(attrs[count].value.ipaddr.addr.ip6, + &flow_overlay_data->sip_mask, sizeof(sai_ip6_t)); + count++; + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_OVERLAY_DATA_DIP_MASK; + attrs[count].value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + clib_memcpy_fast(attrs[count].value.ipaddr.addr.ip6, + &flow_overlay_data->dip_mask, sizeof(sai_ip6_t)); + count++; + } else { + u8 mac[6] = {0}; + /* set default value for bmv2 table */ + attrs[count].id = SAI_FLOW_ENTRY_ATTR_OVERLAY_DATA_DST_MAC; + clib_memcpy_fast(attrs[count].value.mac, mac, sizeof(ethernet_header_t)); + count++; + } + + /* Attrs for encap data */ + if (flow_data->routing_actions & htonl(SAI_DASH_ROUTING_ACTIONS_STATIC_ENCAP)) { + attrs[count].id = SAI_FLOW_ENTRY_ATTR_ENCAP_DATA_VNI; + attrs[count++].value.u32 = (flow_encap_data->vni_high << 16) | ntohs(flow_encap_data->vni_low); + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_ENCAP_DATA_DEST_VNET_VNI; + attrs[count++].value.u32 = (flow_encap_data->dest_vnet_vni_high << 16) | ntohs(flow_encap_data->dest_vnet_vni_low); + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_ENCAP_DATA_UNDERLAY_SIP; + attrs[count++].value.u32 = ntohl(flow_encap_data->underlay_sip.as_u32); + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_ENCAP_DATA_UNDERLAY_DIP; + attrs[count++].value.u32 = ntohl(flow_encap_data->underlay_dip.as_u32); + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_ENCAP_DATA_UNDERLAY_SMAC; + clib_memcpy_fast(attrs[count].value.mac, flow_encap_data->underlay_smac, sizeof(ethernet_header_t)); + count++; + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_ENCAP_DATA_UNDERLAY_DMAC; + clib_memcpy_fast(attrs[count].value.mac, flow_encap_data->underlay_dmac, sizeof(ethernet_header_t)); + count++; + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_ENCAP_DATA_DASH_ENCAPSULATION; + attrs[count++].value.s32 = ntohs(flow_encap_data->dash_encapsulation); + } else { + u8 mac[6] = {0}; + /* set default value for bmv2 table */ + attrs[count].id = SAI_FLOW_ENTRY_ATTR_ENCAP_DATA_UNDERLAY_SMAC; + clib_memcpy_fast(attrs[count].value.mac, mac, sizeof(ethernet_header_t)); + count++; + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_ENCAP_DATA_UNDERLAY_DMAC; + clib_memcpy_fast(attrs[count].value.mac, mac, sizeof(ethernet_header_t)); + count++; + } + + /* Attrs for tunnel data */ + if (flow_data->tunnel_id != 0) { + attrs[count].id = SAI_FLOW_ENTRY_ATTR_TUNNEL_DATA_VNI; + attrs[count++].value.u32 = (flow_tunnel_data->vni_high << 16) | ntohs(flow_tunnel_data->vni_low); + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_TUNNEL_DATA_DEST_VNET_VNI; + attrs[count++].value.u32 = (flow_tunnel_data->dest_vnet_vni_high << 16) | ntohs(flow_tunnel_data->dest_vnet_vni_low); + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_TUNNEL_DATA_UNDERLAY_SIP; + attrs[count++].value.u32 = ntohl(flow_tunnel_data->underlay_sip.as_u32); + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_TUNNEL_DATA_UNDERLAY_DIP; + attrs[count++].value.u32 = ntohl(flow_tunnel_data->underlay_dip.as_u32); + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_TUNNEL_DATA_UNDERLAY_SMAC; + clib_memcpy_fast(attrs[count].value.mac, flow_tunnel_data->underlay_smac, sizeof(ethernet_header_t)); + count++; + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_TUNNEL_DATA_UNDERLAY_DMAC; + clib_memcpy_fast(attrs[count].value.mac, flow_tunnel_data->underlay_dmac, sizeof(ethernet_header_t)); + count++; + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_TUNNEL_DATA_DASH_ENCAPSULATION; + attrs[count++].value.s32 = ntohs(flow_tunnel_data->dash_encapsulation); + } else { + u8 mac[6] = {0}; + /* set default value for bmv2 table */ + attrs[count].id = SAI_FLOW_ENTRY_ATTR_TUNNEL_DATA_UNDERLAY_SMAC; + clib_memcpy_fast(attrs[count].value.mac, mac, sizeof(ethernet_header_t)); + count++; + + attrs[count].id = SAI_FLOW_ENTRY_ATTR_TUNNEL_DATA_UNDERLAY_DMAC; + clib_memcpy_fast(attrs[count].value.mac, mac, sizeof(ethernet_header_t)); + count++; + } + + return dash_flow_api->create_flow_entry(&flow_entry, count, attrs); +} + +sai_status_t +dash_sai_remove_flow_entry (const dash_flow_entry_t *flow) +{ + sai_flow_entry_t flow_entry; + const flow_key_t *flow_key = &flow->key; + + flow_entry.switch_id = dash_switch_id; + clib_memcpy_fast(flow_entry.eni_mac, flow_key->eni_mac, sizeof(flow_entry.eni_mac)); + flow_entry.vnet_id = ntohs(flow_key->vnet_id); + + if (flow_key->is_ip_v6) { + flow_entry.src_ip.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + clib_memcpy_fast(flow_entry.src_ip.addr.ip6, &flow_key->src_ip.ip6, sizeof(sai_ip6_t)); + flow_entry.dst_ip.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + clib_memcpy_fast(flow_entry.dst_ip.addr.ip6, &flow_key->dst_ip.ip6, sizeof(sai_ip6_t)); + } else { + flow_entry.src_ip.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + flow_entry.src_ip.addr.ip4 = flow_key->src_ip.ip4.as_u32; + flow_entry.dst_ip.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + flow_entry.dst_ip.addr.ip4 = flow_key->dst_ip.ip4.as_u32; + } + + flow_entry.src_port = ntohs(flow_key->src_port); + flow_entry.dst_port = ntohs(flow_key->dst_port); + flow_entry.ip_proto = flow_key->ip_proto; + + return dash_flow_api->remove_flow_entry(&flow_entry); +} + diff --git a/dash-pipeline/vpp-plugin/send_p2a_pkt.py b/dash-pipeline/vpp-plugin/send_p2a_pkt.py new file mode 100755 index 000000000..63a8084ec --- /dev/null +++ b/dash-pipeline/vpp-plugin/send_p2a_pkt.py @@ -0,0 +1,75 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from scapy.all import * + +class DASH_PACKET_META(Packet): + name = "DASH_PACKET_META" + fields_desc = [ ByteField("packet_source", 0), + BitField("packet_type", 0, 4), + BitField("packet_subtype", 1, 4), + ShortField("length", 4), + ] + +class DASH_FLOW_KEY(Packet): + name = "DASH_FLOW_KEY" + fields_desc = [ MACField("eni_mac", "0:0:0:0:0:0"), + ShortField("vnet_id", 2), + IP6Field("src_ip", "::1.1.1.1"), + IP6Field("dst_ip", "::2.2.2.2"), + XShortField("src_port", 0x5566), + XShortField("dst_port", 0x6677), + ByteEnumField("ip_proto", 6, IP_PROTOS), + BitField("reserved", 0, 7), + BitField("is_ip_v6", 0, 1), + ] + +class DASH_FLOW_DATA(Packet): + name = "DASH_FLOW_DATA" + fields_desc = [ XByteField("is_unidirectional", 0), + IntField("version", 0), + ShortEnumField("direction", 1, { 1: "OUTBOUND", 2: "INBOUND" }), + ShortField("tunnel_id", 0), + IntField("routing_actions", 0), + IntField("meter_class", 0), + ] + +class DASH_OVERLAY_DATA(Packet): + name = "DASH_OVERLAY_DATA" + fields_desc = [ MACField("dmac", 0), + IP6Field("sip", "::"), + IP6Field("dip", "::"), + IP6Field("sip_mask", "::"), + IP6Field("dip_mask", "::"), + BitField("reserved", 0, 7), + BitField("is_ipv6", 0, 1), + ] + +class DASH_ENCAP_DATA(Packet): + name = "DASH_ENCAP_DATA" + fields_desc = [ BitField("vni", 1, 24), + BitField("dest_vnet_vni", 2, 24), + IPField("underlay_sip", "1.1.1.1"), + IPField("underlay_dip", "2.2.2.2"), + MACField("underlay_smac", "0:0:0:0:0:0"), + MACField("underlay_dmac", "0:0:0:0:0:0"), + ShortField("dash_encapsulation", 1), + ] + + +dpappEther = Ether(dst="02:fe:23:f0:e4:13",src="00:01:01:01:01:01",type=0x876D) + +packetMeta = DASH_PACKET_META() +flowKey = DASH_FLOW_KEY() +flowData = DASH_FLOW_DATA() +packetMeta.length = len(packetMeta) + len(flowKey) + len(flowData) +dashMeta = packetMeta/flowKey/flowData + +customerPacket = Ether(dst="00:02:02:02:02:02",type=0x0800) / \ + IP(src = "10.1.0.10", dst="10.1.1.1")/TCP(sport=4096, dport=4096)/("a"*64) + +pkt = dpappEther/dashMeta/customerPacket +import pdb; pdb.set_trace() +sendp(pkt, iface="veth4", count=1) + diff --git a/dash-pipeline/vpp-plugin/startup.conf b/dash-pipeline/vpp-plugin/startup.conf new file mode 100644 index 000000000..0a1e40550 --- /dev/null +++ b/dash-pipeline/vpp-plugin/startup.conf @@ -0,0 +1,38 @@ +unix { + nodaemon + log /var/log/vpp/vpp.log + full-coredump + cli-listen /run/vpp/cli.sock + gid vpp +} + +api-trace { + on +} + +api-segment { + gid vpp +} + +socksvr { + default +} + +logging { + default-log-level info + default-syslog-log-level info +} + +cpu { + main-core 1 + corelist-workers 2-3 +} + +dpdk { + no-pci +} + +plugins { + add-path /dash/dash-pipeline/vpp-plugin/build/lib/vpp_plugins +} + diff --git a/dash-pipeline/vpp-plugin/vpp.sh b/dash-pipeline/vpp-plugin/vpp.sh new file mode 100755 index 000000000..235fa41e2 --- /dev/null +++ b/dash-pipeline/vpp-plugin/vpp.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +[ -d /var/log/vpp ] || mkdir -p /var/log/vpp + +sysctl vm.nr_hugepages=512 +sysctl vm.max_map_count=1548 + +/usr/bin/vpp -c ${1:-/etc/vpp/startup.conf} & +sleep 3 + +# Create a host interface which connects p4 bmv2 simple_switch +HOST_INTERFACE=${HOST_INTERFACE:-veth5} +vppctl create host-interface name $HOST_INTERFACE +vppctl set interface mac address host-$HOST_INTERFACE 02:fe:23:f0:e4:13 +vppctl set interface state host-$HOST_INTERFACE up +vppctl set interface promiscuous on host-$HOST_INTERFACE +vppctl